طراحی اسکیمای MongoDB برای SaaS چندمستأجری
تصمیم embed در برابر reference، مدل تننت، ایندکسهای ترکیبی، و الگوهایی که از رشد داده تا گزارشگیری دوام میآورند.
علی مرتضوی
بنیانگذار پارادایس کد
اسکیما در MongoDB یعنی الگوی دسترسی
سندگرا بودن مجوز بینظمی نمیدهد. هر کالکشن باید حول پرسشهای پرتکرار محصول شکل بگیرد: «داشبورد تننت چه میخواند؟» «لیست سفارش با چه فیلتری؟» اگر اسکیما را مثل جدول نرمالشده طراحی کنید و در اپ join دستی بسازید، هم پیچیدگی رابطهای را دارید هم مزایای سند را از دست میدهید.
قبل از مدل، ۱۰ کوئری اول محصول را بنویسید. اسکیما پاسخ به آن فهرست است.
Embed در برابر reference
Embed برای دادهای که با والد خوانده/نوشته میشود و کران رشد دارد مناسب است: آدرسهای کاربر، آیتمهای یک سفارش تازه. Reference وقتی لازم است که موجودیت مستقل، زیاد رشدکننده، یا چندوالد داشته باشد: محصولات، کاربران، فایلها.
حد سند ۱۶MB سقف امنیتی نیست؛ سقف طراحی است. آرایههای بیکران (لاگ رویداد داخل سند کاربر) بعداً دردناک میشوند. برای رشد نامحدود، کالکشن فرزند با کلید والد بسازید.
مدل چندمستأجری
رایجترین مدل pragmatic: یک کلاستر، `tenantId` روی هر سند، ایندکس پیشرو با `tenantId`. جداسازی دیتابیس بهازای تننت فقط برای مشتریان enterprise با نیاز isolation قوی یا compliance خاص بماند—هزینه عملیات را چند برابر میکند.
هر کوئری باید تننت را اجبار کند. در لایهٔ repository، فیلتر تننت را غیرقابل دور زدن کنید تا باگ IDOR به مدل داده نشت نکند. برای دادههای مشترک پلتفرم (پلنها، ترجمهها) کالکشن جدا بدون تننت نگه دارید.
ایندکسهایی که SaaS را زنده نگه میدارند
ایندکس ترکیبی را با پیشوند تننت بسازید: `{ tenantId: 1, createdAt: -1 }`، `{ tenantId: 1, status: 1, updatedAt: -1 }`. Unique هم معمولاً مرکب است: ایمیل یکتا در سطح تننت نه لزوماً در کل پلتفرم—مگر محصول طور دیگری بگوید.
TTL برای دادههای موقت (سشن، کد OTP، رویداد خام) هزینه را کنترل میکند. ایندکس بلااستفاده را با `$indexStats` پیدا و حذف کنید؛ نوشتن ارزان نیست.
تراکنش، ثبات و واقعیت توزیعشده
تراکنش چندسندی وجود دارد، اما گران است و نباید عادت هر write باشد. اول مدل را طوری طراحی کنید که یک سند حقیقت واحد یک عملیات باشد. اگر باید چند سند عوض شوند، invariant و جبران (compensating action) را روشن تعریف کنید.
برای شمارندهها و موجودی، یا از عملیات اتمی سند استفاده کنید یا صف/outbox؛ race شرطی در SaaS پولی سریع به خسارت تبدیل میشود.
رشد، آرشیو و گزارش
دادههای داغ و سرد را جدا کنید. رویدادهای تحلیلی را در مسیر آنلاین نگه ندارید؛ به pipeline یا کالکشن آرشیو بفرستید. برای گزارش تننت، پیشتجمیع شبانه یا تغییر stream بهتر از aggregate سنگین روی درخواست کاربر است.
نسخهگذاری اسکیما را جدی بگیرید: فیلد جدید اختیاری، مهاجرت تدریجی، و خواندن دفاعی در کد. اسکیمای بدون نسخه در ماه ششم قفل تغییر میشود.
چکلیست مدل قبل از لانچ
مالک هر کالکشن، ۱۰ کوئری اول، ایندکسها، سقف رشد آرایهها، و سیاست حذف/TTL را مکتوب کنید. اگر یکی از اینها مبهم است، اسکیما هنوز آمادهٔ SaaS نیست.
مدل داده خوب دیده نمیشود—تا روزی که بد باشد. سرمایهگذاری زودهنگام اینجا ارزانتر از بازنویسی سال بعد است.
سؤالات متداول
آیا باید همیشه از تراکنش MongoDB استفاده کنیم؟
خیر. اول مدل تکسندی اتمی را ترجیح دهید. تراکنش را برای invariantهای واقعاً چندسندی نگه دارید.
embed آدرس داخل کاربر درست است؟
اگر تعداد محدود و با پروفایل خوانده میشود، بله. اگر آدرس موجودیت مستقل با تاریخچه و اشتراک بین موجودیتهاست، reference کنید.
یک دیتابیس برای هر مشتری؟
فقط برای نیاز isolation/compliance خاص. برای اکثر SaaSها، `tenantId` با ایندکس و کنترل دسترسی کافی و عملیاتیتر است.
چطور از document growth جلوگیری کنیم؟
آرایههای unbounded را به کالکشن فرزند منتقل کنید، TTL/آرشیو بگذارید، و سقف رشد را در review مدل چک کنید.
دانش و مقالات
نیاز به اجرای همین مفاهیم در محصولتان دارید؟
پارادایس کد از مشاوره تا پیادهسازی کامل کنار شماست.