জ্যাঙ্গো একটি জনপ্রিয় ফ্রেমওয়ার্ক যা আপনি আপনার কোম্পানির জন্য একটি অ্যাপ্লিকেশন বিকাশ করতে নির্বাচন করতে পারেন। কিন্তু আপনি যদি একটি SaaS অ্যাপ্লিকেশন তৈরি করতে চান যা একাধিক ক্লায়েন্ট ব্যবহার করবে? আপনি কি স্থাপত্য চয়ন করা উচিত? আসুন দেখি কিভাবে এই কাজটি করা যায়।
আপনার কাছে থাকা প্রতিটি ক্লায়েন্টের জন্য একটি পৃথক উদাহরণ তৈরি করা সবচেয়ে সহজ পদ্ধতি। ধরা যাক আমাদের একটি জ্যাঙ্গো অ্যাপ্লিকেশন এবং একটি ডাটাবেস আছে। তারপর, প্রতিটি ক্লায়েন্টের জন্য, আমাদের নিজস্ব ডাটাবেস এবং অ্যাপ্লিকেশন উদাহরণ চালাতে হবে। এর মানে হল যে প্রতিটি আবেদনের উদাহরণে শুধুমাত্র একজন ভাড়াটে থাকে।
এই পদ্ধতিটি বাস্তবায়ন করা সহজ: আপনাকে আপনার কাছে থাকা প্রতিটি পরিষেবার একটি নতুন উদাহরণ শুরু করতে হবে। কিন্তু একই সময়ে, এটি একটি সমস্যা সৃষ্টি করতে পারে: প্রতিটি ক্লায়েন্ট অবকাঠামোর খরচ উল্লেখযোগ্যভাবে বৃদ্ধি করবে। আপনি যদি মাত্র কয়েকটি ক্লায়েন্ট রাখার পরিকল্পনা করেন বা প্রতিটি উদাহরণ ছোট হয় তবে এটি একটি বড় ব্যাপার নাও হতে পারে।
যাইহোক, ধরা যাক যে আমরা একটি বড় কোম্পানি তৈরি করছি যেটি 100,000 প্রতিষ্ঠানকে একটি কর্পোরেট মেসেঞ্জার প্রদান করে। কল্পনা করুন, প্রতিটি নতুন ক্লায়েন্টের জন্য পুরো পরিকাঠামো নকল করা কতটা ব্যয়বহুল হতে পারে! এবং, যখন আমাদের অ্যাপ্লিকেশন সংস্করণ আপডেট করতে হবে, তখন আমাদের প্রতিটি ক্লায়েন্টের জন্য এটি স্থাপন করতে হবে, তাই স্থাপনাটিও ধীর হয়ে যাবে ৷
আরেকটি পদ্ধতি আছে যা একটি পরিস্থিতিতে সাহায্য করতে পারে যখন আমাদের কাছে অ্যাপ্লিকেশনের জন্য প্রচুর ক্লায়েন্ট থাকে: একটি মাল্টি-টেন্যান্ট আর্কিটেকচার। এর মানে হল যে আমাদের একাধিক ক্লায়েন্ট আছে, যাকে আমরা ভাড়াটে বলি, কিন্তু তারা সবাই অ্যাপ্লিকেশনের একটি মাত্র উদাহরণ ব্যবহার করে।
যদিও এই আর্কিটেকচারটি প্রতিটি ক্লায়েন্টের জন্য ডেডিকেটেড দৃষ্টান্তের উচ্চ খরচের সমস্যা সমাধান করে, এটি একটি নতুন সমস্যা প্রবর্তন করে: কীভাবে আমরা নিশ্চিত হতে পারি যে ক্লায়েন্টের ডেটা অন্য ক্লায়েন্টদের থেকে নিরাপদে বিচ্ছিন্ন হয়েছে?
আমরা নিম্নলিখিত পদ্ধতিগুলি নিয়ে আলোচনা করব:
একটি শেয়ার্ড ডাটাবেস এবং শেয়ার্ড ডাটাবেস স্কিমা ব্যবহার করে: আমরা প্রতিটি ডাটাবেস টেবিলে যোগ করতে হবে এমন বিদেশী কী দ্বারা কোন ভাড়াটে ডেটার মালিক তা শনাক্ত করতে পারি।
একটি ভাগ করা ডাটাবেস ব্যবহার করা, কিন্তু পৃথক ডাটাবেস স্কিমা : এইভাবে, আমাদের একাধিক ডাটাবেস দৃষ্টান্ত বজায় রাখতে হবে না তবে ভাড়াটে ডেটা বিচ্ছিন্নতার একটি ভাল স্তর পাবেন।
পৃথক ডাটাবেস ব্যবহার করা: এটি একক-ভাড়াটেদের উদাহরণের মতো দেখায়, কিন্তু একই রকম হবে না, কারণ আমরা এখনও একটি ভাগ করা অ্যাপ্লিকেশন উদাহরণ ব্যবহার করব এবং ভাড়াটে চেক করে কোন ডাটাবেস ব্যবহার করতে হবে তা নির্বাচন করব৷
আসুন এই ধারণাগুলির আরও গভীরে ডুব দেওয়া যাক এবং দেখুন কিভাবে জ্যাঙ্গো অ্যাপ্লিকেশনের সাথে তাদের একীভূত করা যায়।
এই বিকল্পটি প্রথম মনে আসতে পারে: টেবিলে একটি ForeignKey যোগ করতে এবং প্রতিটি ভাড়াটেদের জন্য উপযুক্ত ডেটা নির্বাচন করতে এটি ব্যবহার করুন। যাইহোক, এর একটি বিশাল অসুবিধা রয়েছে: ভাড়াটেদের ডেটা একেবারেই আলাদা করা হয় না, তাই একটি ছোট প্রোগ্রামিং ত্রুটি ভাড়াটেদের ডেটা ভুল ক্লায়েন্টের কাছে ফাঁস করার জন্য যথেষ্ট হতে পারে।
জ্যাঙ্গো ডকুমেন্টেশন থেকে ডাটাবেস গঠনের একটি উদাহরণ নেওয়া যাক:
from django.db import models class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField("date published") class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0)
আমাদের চিহ্নিত করতে হবে কোন রেকর্ডগুলো কোন ভাড়াটিয়ার মালিকানাধীন। সুতরাং, প্রতিটি বিদ্যমান টেবিলে আমাদের একটি Tenant
টেবিল এবং একটি বিদেশী কী যোগ করতে হবে:
class Tenant(models.Model): name = models.CharField(max_length=200) class Question(models.Model): tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE) question_text = models.CharField(max_length=200) pub_date = models.DateTimeField("date published") class Choice(models.Model): tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE) question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0)
কোডটিকে কিছুটা সরল করার জন্য, আমরা একটি বিমূর্ত বেস মডেল তৈরি করতে পারি যা আমাদের তৈরি করা একে অপরের মডেলে পুনরায় ব্যবহার করা হবে।
class Tenant(models.Model): name = models.CharField(max_length=200) class BaseModel(models.Model): tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE) class Meta: abstract = True class Question(BaseModel): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField("date published") class Choice(BaseModel): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0)
আপনি দেখতে পাচ্ছেন, এখানে কমপক্ষে দুটি বড় ঝুঁকি রয়েছে: একজন বিকাশকারী নতুন মডেলে একটি ভাড়াটে ক্ষেত্র যোগ করতে ভুলে যেতে পারেন , অথবা একজন বিকাশকারী ডেটা ফিল্টার করার সময় এই ক্ষেত্রটি ব্যবহার করতে ভুলে যেতে পারেন।
এই উদাহরণের সোর্স কোডটি GitHub-এ পাওয়া যাবে: https://github.com/bp72/django-multitenancy-examples/tree/main/01_shared_database_shared_schema ।
শেয়ার্ড স্কিমার ঝুঁকির কথা মাথায় রেখে, আরেকটি বিকল্প বিবেচনা করা যাক: ডাটাবেস এখনও শেয়ার করা হবে, কিন্তু আমরা প্রতিটি ভাড়াটেদের জন্য একটি ডেডিকেটেড স্কিমা তৈরি করব। বাস্তবায়নের জন্য, আমরা একটি জনপ্রিয় লাইব্রেরি জ্যাঙ্গো-টেন্যান্ট ( ডকুমেন্টেশন ) দেখতে পারি।
আসুন আমাদের ছোট প্রকল্পে django-tenants
যোগ করি (অফিসিয়াল ইনস্টলেশন পদক্ষেপগুলি এখানে পাওয়া যাবে)।
প্রথম ধাপ হল pip
মাধ্যমে লাইব্রেরি ইনস্টলেশন:
pip install django-tenants
মডেলগুলি পরিবর্তন করুন: Tenant
মডেলটি এখন একটি পৃথক অ্যাপে থাকবে Question
এবং Choice
মডেলের আর ভাড়াটেদের সাথে কোনো সংযোগ থাকবে না৷ যেহেতু বিভিন্ন ভাড়াটেদের ডেটা আলাদা স্কিমাতে থাকবে, তাই আমাদের আর ভাড়াটেদের সারিগুলির সাথে পৃথক রেকর্ডগুলি লিঙ্ক করার প্রয়োজন হবে না।
ফাইল tenants/models.py
from django.db import models from django_tenants.models import TenantMixin, DomainMixin class Tenant(TenantMixin): name = models.CharField(max_length=200) # default true, schema will be automatically created and synced when it is saved auto_create_schema = True class Domain(DomainMixin): # a required table for django-tenants too ...
ফাইল polls/models.py
from django.db import models class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField("date published") class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0)
লক্ষ্য করুন যে প্রশ্ন এবং পছন্দের টেন্যান্টের কাছে আর বিদেশী কী নেই!
অন্য যে জিনিসটি পরিবর্তন করা হয়েছে তা হল টেন্যান্ট এখন একটি আলাদা অ্যাপে রয়েছে: এটি শুধুমাত্র ডোমেনগুলিকে আলাদা করার জন্য নয়, এটি গুরুত্বপূর্ণও কারণ আমাদের শেয়ার্ড স্কিমাতে tenants
টেবিল সংরক্ষণ করতে হবে এবং প্রতিটি ভাড়াটেদের জন্য polls
টেবিল তৈরি করা হবে স্কিমা
একাধিক স্কিমা এবং ভাড়াটেদের সমর্থন করতে settings.py
ফাইলে পরিবর্তন করুন:
DATABASES = { 'default': { 'ENGINE': 'django_tenants.postgresql_backend', # .. } } DATABASE_ROUTERS = ( 'django_tenants.routers.TenantSyncRouter', ) MIDDLEWARE = ( 'django_tenants.middleware.main.TenantMainMiddleware', #... ) TEMPLATES = [ { #... 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.request', #... ], }, }, ] SHARED_APPS = ( 'django_tenants', # mandatory 'tenants', # you must list the app where your tenant model resides in 'django.contrib.contenttypes', # everything below here is optional 'django.contrib.auth', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.messages', 'django.contrib.admin', ) TENANT_APPS = ( # your tenant-specific apps 'polls', ) INSTALLED_APPS = list(SHARED_APPS) + [app for app in TENANT_APPS if app not in SHARED_APPS] TENANT_MODEL = "tenants.Tenant" TENANT_DOMAIN_MODEL = "tenants.Domain"
এর পরে, আসুন মাইগ্রেশন তৈরি এবং প্রয়োগ করি:
python manage.py makemigrations
python manage.py migrate_schemas --shared
ফলস্বরূপ, আমরা দেখব যে সর্বজনীন স্কিমা তৈরি হবে এবং এতে শুধুমাত্র ভাগ করা টেবিল থাকবে।
আমাদের public
স্কিমার জন্য একটি ডিফল্ট ভাড়াটে তৈরি করতে হবে:
python manage.py create_tenant --domain-domain=default.com --schema_name=public --name=default_tenant
জিজ্ঞাসা করা হলে is_primary
কে True
এ সেট করুন।
এবং তারপর, আমরা পরিষেবার প্রকৃত ভাড়াটে তৈরি করা শুরু করতে পারি:
python manage.py create_tenant --domain-domain=tenant1.com --schema_name=tenant1 --name=tenant_1 python manage.py create_tenant --domain-domain=tenant2.com --schema_name=tenant2 --name=tenant_2
লক্ষ্য করুন যে ডাটাবেসে এখন আরও 2টি স্কিমা রয়েছে যাতে polls
টেবিল রয়েছে:
এখন, আপনি বিভিন্ন স্কিমা থেকে প্রশ্ন এবং পছন্দ পাবেন যখন আপনি ভাড়াটেদের জন্য সেট আপ করেছেন এমন ডোমেনে API কল করবেন - সব শেষ!
যদিও সেটআপটি আরও জটিল এবং সম্ভবত আরও কঠিন দেখায় যদি আপনি বিদ্যমান অ্যাপটি স্থানান্তরিত করেন, তবে পদ্ধতিতে এখনও ডেটার নিরাপত্তার মতো অনেক সুবিধা রয়েছে।
উদাহরণের কোড এখানে পাওয়া যাবে।
আজকে আমরা যে শেষ পন্থা নিয়ে আলোচনা করব তা হল আরও এগিয়ে যাওয়া এবং ভাড়াটেদের জন্য আলাদা ডেটাবেস থাকা।
এই সময়, আমাদের কাছে কয়েকটি ডাটাবেস থাকবে:
আমরা ভাগ করা ডেটা সংরক্ষণ করব যেমন টেন্যান্টের ম্যাপিং ডেটাবেসগুলির নামের সাথে default_db
এবং প্রতিটি ভাড়াটে জন্য একটি পৃথক ডাটাবেস তৈরি করব৷
তারপরে আমাদের settings.py-এ ডেটাবেস কনফিগারেশন সেট করতে হবে:
DATABASES = { 'default': { 'NAME': 'default_db', ... }, 'tenant_1': { 'NAME': 'tenant_1', ... }, 'tenant_2': { 'NAME': 'tenant_2', ... }, }
এবং এখন, আমরা QuerySet পদ্ধতি using
কল করে প্রতিটি ভাড়াটেদের জন্য ডেটা পেতে সক্ষম হব:
Questions.objects.using('tenant_1')…
পদ্ধতির নেতিবাচক দিক হল যে আপনাকে ব্যবহার করে প্রতিটি ডাটাবেসে সমস্ত মাইগ্রেশন প্রয়োগ করতে হবে:
python manage.py migrate --database=tenant_1
django-tenants
ব্যবহারের তুলনায় বা শেয়ার্ড স্কিমা পদ্ধতির মতো একটি বিদেশী কী ব্যবহার করার তুলনায় প্রতিটি ভাড়াটেদের জন্য একটি নতুন ডাটাবেস তৈরি করা কম সুবিধাজনক হতে পারে।
অন্যদিকে, ভাড়াটেদের ডেটার বিচ্ছিন্নতা সত্যিই ভাল: ডেটাবেসগুলি শারীরিকভাবে আলাদা করা যেতে পারে। আরেকটি সুবিধা হল যে আমরা শুধুমাত্র Postgresql ব্যবহার করে সীমাবদ্ধ থাকব না কারণ এটি django-tenants
জন্য প্রয়োজন, আমরা আমাদের প্রয়োজন অনুসারে যে কোনও ইঞ্জিন নির্বাচন করতে পারি।
একাধিক ডাটাবেসের বিষয়ে আরও তথ্য জ্যাঙ্গো ডকুমেন্টেশনে পাওয়া যাবে।
| একক ভাড়াটে | শেয়ার্ড স্কিমা সহ MT | পৃথক স্কিমা সহ MT | পৃথক ডাটাবেস সহ MT |
---|---|---|---|---|
ডেটা বিচ্ছিন্নতা | ✅উচ্চ | ❌ সর্বনিম্ন | ✅উচ্চ | ✅উচ্চ |
ভুলবশত ডেটা ফাঁস হওয়ার ঝুঁকি | ✅ কম | ❌উচ্চ | ✅ কম | ✅ কম |
অবকাঠামো খরচ | ❌প্রতিটি ভাড়াটিয়ার সাথে উচ্চতর | ✅নিম্ন | ✅নিম্ন | ✅❌ একক ভাড়াটে থেকে কম |
স্থাপনার গতি | ❌প্রতিটি ভাড়াটিয়ার সাথে কম | ✅ | ✅❌ মাইগ্রেশন ধীর হবে কারণ সেগুলি প্রতিটি স্কিমার জন্য কার্যকর করা প্রয়োজন৷ | ✅❌ মাইগ্রেশন ধীর হবে কারণ সেগুলি প্রতিটি ডাটাবেসের জন্য কার্যকর করা প্রয়োজন৷ |
বাস্তবায়ন সহজ | ✅ | ❌ যদি পরিষেবাটি ইতিমধ্যে একটি একক ভাড়াটে অ্যাপ হিসাবে প্রয়োগ করা হয় তবে অনেক পরিবর্তনের প্রয়োজন৷ | ✅ | ✅ |
উপরের সমস্তগুলিকে সংক্ষিপ্ত করার জন্য, মনে হচ্ছে সমস্যাটির জন্য কোনও সিলভার বুলেট নেই, প্রতিটি পদ্ধতিরই তার সুবিধা এবং অসুবিধা রয়েছে, তাই এটি বিকাশকারীদের উপর নির্ভর করে যে তারা কী ট্রেড-অফ করতে পারে তা নির্ধারণ করা।
পৃথক ডাটাবেস ভাড়াটেদের ডেটার জন্য সর্বোত্তম বিচ্ছিন্নতা প্রদান করে এবং প্রয়োগ করা সহজ, তবে, এটি রক্ষণাবেক্ষণের জন্য আপনার বেশি খরচ করে: n ডেটাবেস আপডেট করতে, ডেটাবেস সংযোগের সংখ্যা বেশি।
বাস্তবায়নের জন্য একটি পৃথক স্কিমা বিট জটিল সহ একটি ভাগ করা ডাটাবেস এবং মাইগ্রেশনের সাথে কিছু সমস্যা হতে পারে।
একক ভাড়াটে বাস্তবায়ন করা সবচেয়ে সহজ, কিন্তু আপনার কাছে প্রতি ভাড়াটে আপনার পরিষেবার সম্পূর্ণ অনুলিপি থাকায় সম্পদের অতিরিক্ত খরচের জন্য এটি আপনাকে খরচ করে।