কম্পিউটার টিউটোরিয়াল

ActiveRecord বনাম Ecto: ডেটাবেস অ্যাবস্ট্রাকশনের জন্য একটি ডেভেলপার গাইড - পর্ব এক

ডেটা অধিকাংশ সফটওয়্যার অ্যাপ্লিকেশনের মূল অংশ। ডেটাবেস থেকে ডেটা ম্যাপ করা এবং কোয়ারি করা একটি ডেভেলপারের জীবনে বারবার অনুষ্ঠিত কাজ। এইজন্য, প্রক্রিয়াটি বোঝা এবং কাজটি সহজ করতে পারা অ্যাবস্ট্রাকশন ব্যবহার করা জরুরি।

এই পোস্টে, যা দুটি পর্বের প্রথম পর্ব, আপনি ActiveRecord (রুবি) এবং Ecto (ইলিক্সির) এর মধ্যে তুলনা পাবেন। আমরা দেখব কীভাবে দুটো টুলই ডেভেলপারদের ডেটাবেস স্কিমা মাইগ্রেট এবং ম্যাপ করতে সক্ষম করে।

তাই আমরা আপেল এবং কমলাবগানের তুলনা করছি। (মূল) ব্যাটগার্ল, যাকে কখনো কিছু বলতে হয়নি, বনাম ব্যাটম্যান, যিনি স্পষ্টভাবে বলেন 'আমি ব্যাটম্যান'। নিঃশব্দ, কনভেনশন ওভার কনফিগারেশন, বনাম স্পষ্ট উদ্দেশ্য। রাউন্ড वन। ফাইট!

ActiveRecord

রিলিজের ১০ বছরের বেশি সময় পার হোক, संभावনা আছে আপনি ইতিমধ্যে ActiveRecord সম্পর্কে শুনেছেন - বিখ্যাত ORM যা রুবি অন রেইলস প্রজেক্টের সাথে ডিফল্টভাবে আসে।

ActiveRecord MVC-র M - মডেল - যা সিস্টেমের वो লেয়ার যা ব্যবসায়িক ডেটা এবং লজিক রিপ্রেজেন্ট করার জন্য দায়ী। ActiveRecord ব্যবসায়িক অবজেক্ট তৈরি এবং ব্যবহার করা সহজ করে যাদের ডেটা ডেটাবেসে 지속 সংরক্ষণের প্রয়োজন। এটি ActiveRecord প্যাটার্নের একটি ইমপ্লিমেন্টেশন যা নিজে একটি অবজেক্ট রিলেশনাল ম্যাপিং সিস্টেমের বর্ণনা।

যদিও এটি মূলত রেইলসের সাথে ব্যবহারের জন্য পরিচিত, ActiveRecord একটি স্বতন্ত্র টুল হিসেবে অন্যান্য প্রজেক্টে এম্বেডেড ব্যবহার করা যাবে।

Ecto

ActiveRecord-এর সাথে তুলনা করলে, Ecto একটি যথেষ্ট নতুন (এবং ফিলহাল এত বিখ্যাত নয়) টুল। এটি ইলিক্সিতে লেখা এবং ফিনিক্স প্রজেক্টের সাথে ডিফল্টভাবে থাকে।

ActiveRecord-এর বিপরীতে, Ecto একটি ORM নয়, বরং একটি লাইব্রেরি যা ইলিক্সি ব্যবহার করে কোয়ারি লিখতে এবং ডেটাবেসের সাথে ইন্টারঅ্যাক্ট করতে সক্ষম করে।

Ecto ইলিক্সিতে কোয়ারি লিখতে এবং ডেটাবেসের সাথে ইন্টারঅ্যাক্ট করার জন্য একটি ডোমেন স্পেসিফিক ল্যাঙ্গুয়েজ।

ডিজাইন অনুযায়ী, Ecto একটি স্বতন্ত্র টুল, বিভিন্ন ইলিক্সি প্রজেক্টে ব্যবহৃত হয় এবং যেকোনো ফ্রেমওয়ার্কের সাথে সংযুক্ত নয়।

কি আপনি আপেল এবং কমলাবগানের তুলনা করছেন?

হ্যাঁ, আমরা করছি! যদিও ActiveRecord এবং Ecto সেম্যান্টিক্যালি ভিন্ন, কিন্তু ডেটাবেস মাইগ্রেশন, ডেটাবেস ম্যাপিং, কোয়ারি এবং ভ্যালিডেশন সাধারণ বৈশিষ্ট্যগুলো উভয়ই সমর্থন করে। এবং দুটো টুলই ব্যবহার করে একই ফলাফল অর্জন করা যায়। যারা রুবি ব্যাকগ্রাউন্ড থেকে ইলিক্সিতে আগ্রহী, তাদের জন্য আমরা মনে করি এটি একটি আকর্ষণীয় তুলনা হবে।

ইনভয়েস সিস্টেম

পোস্টের বাকি অংশে, একটি ধারণামূলক ইনভয়েস সিস্টেম ডেমোسترেশনের জন্য ব্যবহার করা হবে। চিন্তা করুন আমাদের একটি স্টোর আছে যা সুপার হিরোদের সুট বিক্রি করে। সাদگیর জন্য, ইনভয়েস সিস্টেমের জন্য আমাদের শুধু দুটি টেবিল থাকবে: users এবং invoices।

নিচে সেগুলোর কাঠামো, ফিল্ড এবং টাইপসহ দেওয়া হলো:

users

FieldType
full_namestring
emailstring
created_at (ActiveRecord) / inserted_at (Ecto)datetime
updated_atdatetime

invoices

FieldType
user_idinteger
payment_methodstring
paid_atdatetime
created_at (ActiveRecord) / inserted_at (Ecto)datetime
updated_atdatetime

ইউজারস টেবিলে চারটি ফিল্ড: full_name, email, updated_at এবং একটি চতুর্থ ফিল্ড যা ব্যবহৃত টুলের উপর নির্ভরশীল। ActiveRecord created_at ফিল্ড তৈরি করে যেখানে Ecto inserted_at ফিল্ড তৈরি করে রেকর্ড প্রথমবার ডেটাবেসে ইনসার্ট হওয়ার টাইমস্ট্যাম্প রিপ্রেজেন্ট করতে।

দ্বিতীয় টেবিল নামক invoices। এতে পাঁচটি ফিল্ড: user_id, payment_method, paid_at, updated_at এবং, ইউজারস টেবিলের মতো, created_at অথবা inserted_at, টুলের উপর নির্ভর করে।

ইউজারস এবং ইনভয়েস টেবিলগুলোর নিচের অ্যাসোসিয়েশনগুলো আছে:

  • একজন ইউজারের অনেক ইনভয়েস থাকে
  • একটি ইনভয়েস একটি ইউজারের অন্তর্গত

মাইগ্রেশন

মাইগ্রেশন ডেভেলপারদের তাদের ডেটাবেস স্কিমা সময়ের সাথে সাথে সহজে ডেভেলপ করতে দেয়, একটি ইটেরেটিভ প্রক্রিয়া ব্যবহার করে। ActiveRecord এবং Ecto উভয়ই ডেভেলপারদের হাই-লেভেল ল্যাঙ্গুয়েজ (क्रमশः রুবি এবং ইলিক্সি) ব্যবহার করে ডেটাবেস স্কিমা মাইগ্রেট করতে দেয়, সরাসরি SQL নিয়ে না।

চলুন ActiveRecord এবং Ecto-তে মাইগ্রেশন কীভাবে কাজ করে সেটা দেখি users এবং invoices টেবিল তৈরি করে।

ActiveRecord: Users টেবিল তৈরি

Migration

class CreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
      t.string :full_name, null: false
      t.string :email, index: {unique: true}, null: false
      t.timestamps
    end
  end
end

ActiveRecord মাইগ্রেশন create_table মেথড ব্যবহার করে টেবিল তৈরির অনুমতি দেয়। যদিও created_at এবং updated_at ফিল্ডগুলো মাইগ্রেশন ফাইলে সংজ্ঞায়িত নয়, t.timestamps ব্যবহার করলে ActiveRecord উভয়ই তৈরি করে।

তैयার টেবিল কাঠামো

CreateUsers মাইগ্রেশন চালানোর পর, তৈরি টেবিলের কাঠামো হবে:

 Column | Type | Nullable | Default
------------+-----------------------------+----------+-----------------------------------
 id | bigint | not null | nextval('users_id_seq'::regclass)
 full_name | character varying | not null |
 email | character varying | not null |
 created_at | timestamp without time zone | not null |
 updated_at | timestamp without time zone | not null |
 Indexes:
 "users_pkey" PRIMARY KEY, btree (id)
 "index_users_on_email" UNIQUE, btree (email) 

মাইগ্রেশনটি ইমেইল ফিল্ডের জন্য ইউনিক ইনডেক্স তৈরির জন্যও দায়ী। index: {unique: true} অপশনটি ইমেইল ফিল্ড ডেফিনিশনে পাস করা হয়েছে। এইজন্য টেবিল কাঠামোর অংশ হিসেবে "index_users_on_email" UNIQUE, btree (email) ইনডেক্স লিস্টেড আছে।

Ecto: Users টেবিল তৈরি

Migration

defmodule Financex.Repo.Migrations.CreateUsers do
  use Ecto.Migration
  def change do
    create table(:users) do
      add :full_name, :string, null: false
      add :email, :string, null: false
      timestamps()
    end
    create index(:users, [:email], unique: true)
  end
end

Ecto মাইগ্রেশন create() এবং table() ফাংশন কম্বাইন করে users টেবিল তৈরি করে। Ecto মাইগ্রেশন ফাইলটি তার ActiveRecord সমতুল্যের সাথে খুব মিলता-जুলতা। ActiveRecord-এ টাইমস্ট্যাম্প ফিল্ড (created_at এবং updated_at) t.timestamps দ্বারা তৈরি হয় যেখানে Ecto-তে টাইমস্ট্যাম্প ফিল্ড (inserted_at এবং updated_at) timestamps() ফাংশন দ্বারা তৈরি হয়।

ইনডেক্স তৈরি করার ক্ষেত্রে দুটো টুলের মধ্যে ছোট পার্থক্য আছে। ActiveRecord-এ, ইনডেক্স তৈরি হচ্ছে ফিল্ড তৈরির সময় অপশন হিসেবে। Ecto create() এবং index() ফাংশন কম্বিনেশন ব্যবহার করে সেটি অর্জন করে, টেবিল তৈরির মতো কনসিস্টেন্ট।

তैयার টেবিল কাঠামো

 Column | Type | Nullable | Default
--------------+-----------------------------+----------+-----------------------------------
 id | bigint | not null | nextval('users_id_seq'::regclass)
 full_name | character varying(255) | not null |
 email | character varying(255) | not null |
 inserted_at | timestamp without time zone | not null |
 updated_at | timestamp without time zone | not null |
 Indexes:
 "users_pkey" PRIMARY KEY, btree (id)
 "users_email_index" UNIQUE, btree (email) 

Financex.Repo.Migrations.CreateUsers মাইগ্রেশন চালানোর পর তৈরি টেবিল ActiveRecord ব্যবহার করে তৈরি টেবিলের সাথে একই কাঠামো 갖고।

ActiveRecord: invoices টেবিল তৈরি

Migration

class CreateInvoices < ActiveRecord::Migration[5.2]
  def change
    create_table :invoices do |t|
      t.references :user
      t.string :payment_method
      t.datetime :paid_at
      t.timestamps
    end
  end
end

এই মাইগ্রেশনে t.references মেথড রয়েছে, যা পূর্বের মাইগ্রেশনে ছিল না। এটি users টেবিলের রেফারেন্স তৈরি করতে ব্যবহৃত হয়। যেমন আগে বর্ণনা করা হয়েছে, একজন ইউজারের অনেক ইনভয়েস থাকে এবং একটি ইনভয়েস একজন ইউজারের অন্তর্গত। t.references মেথড invoices টেবিলে একটি user_id কলাম তৈরি করে সেই রেফারেন্স ধারণ করতে।

তैयার টেবিল কাঠামো

 Column | Type | Nullable | Default
----------------+-----------------------------+----------+--------------------------------------
 id | bigint | not null | nextval('invoices_id_seq'::regclass)
 user_id | bigint | |
 payment_method | character varying | |
 paid_at | timestamp without time zone | |
 created_at | timestamp without time zone | not null |
 updated_at | timestamp without time zone | not null |
 Indexes:
 "invoices_pkey" PRIMARY KEY, btree (id)
 "index_invoices_on_user_id" btree (user_id) 

তैयার টেবিল পূর্বে তৈরি টেবিলের মতো একই প্যাটার্ন অনুসরণ করে। একমাত্র পার্থক্য হলো একটি এক্সট্রা ইনডেক্স (index_invoices_on_user_id), যা ActiveRecord অটোমেটিক্যালি যোগ করে যখন t.references মেথড ব্যবহার করা হয়।

Ecto: invoices টেবিল তৈরি

Migration

defmodule Financex.Repo.Migrations.CreateInvoices do
  use Ecto.Migration
  def change do
    create table(:invoices) do
      add :user_id, references(:users)
      add :payment_method, :string
      add :paid_at, :utc_datetime
      timestamps()
    end
    create index(:invoices, [:user_id])
  end
end

Ecto ডেটাবেস রেফারেন্স তৈরিকে সমর্থন করে, references() ফাংশন ব্যবহার করে। ActiveRecord-এর বিপরীতে, যা কলাম নাম অনুমান করে, Ecto ডেভেলপারকে স্পষ্টভাবে user_id কলাম নাম ডিফাইন করতে বাধ্য করে। references() ফাংশনও ডেভেলপারকে স্পষ্টভাবে ডিফাইন করতে বাধ্য করে যে রেফারেন্সটি কোন টেবিলের দিকে নির্দেশ করছে, এই উদাহরণে, users টেবিল।

তैयার টেবিল কাঠামো

 Column | Type | Nullable | Default
----------------+-----------------------------+----------+--------------------------------------
 id | bigint | not null | nextval('invoices_id_seq'::regclass)
 user_id | bigint | |
 payment_method | character varying(255) | |
 paid_at | timestamp without time zone | |
 inserted_at | timestamp without time zone | not null |
 updated_at | timestamp without time zone | not null |
 Indexes:
 "invoices_pkey" PRIMARY KEY, btree (id)
 "invoices_user_id_index" btree (user_id)
 Foreign-key constraints:
 "invoices_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) 

দুই মাইগ্রেশনও যথেষ্ট মিলতা-जুলতা। references ফিচার কীভাবে হ্যান্ডল করা হয় সেটার ক্ষেত্রে কিছু পার্থক্য:

  1. Ecto user_id ফিল্ডের জন্য একটি ফরেন-কি কনস্ট্রেইন্ট তৈরি করে ("invoices_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id)), যা users এবং invoices টেবিলের মধ্যে রেফারেনশিয়াল ইন্টিগ্রিটি বজায় রাখে।
  2. ActiveRecord user_id কলামের জন্য অটোমেটিক্যালি একটি ইনডেক্স তৈরি করে। Ecto ডেভেলপারকে সেটি সম্পর্কে স্পষ্ট হতে বাধ্য করে। এইজন্য মাইগ্রেশনে create index(:invoices, [:user_id]) স্টেটমেন্ট আছে।

ActiveRecord: ডেটা ম্যাপিং এবং অ্যাসোসিয়েশন

ActiveRecord তার "কনভেনশন ওভার কনফিগারেশন" شعار력으로 পরিচিত। এটি মডেল ক্লাসের নাম ব্যবহার করে ডেটাবেস টেবিলের নাম অনুমান করে, ডিফল্টভাবে। User নামক একটি ক্লাস, ডিফল্টভাবে, users টেবিলকে তার সোর্স হিসেবে ব্যবহার করে। ActiveRecord টেবিলের সকল কলামকে ইনস্ট্যান্স অ্যাট্রিবিউট হিসেবে ম্যাপ করে। ডেভেলপারদের শুধু টেবিলগুলোর মধ্যে অ্যাসোসিয়েশন ডিফাইন করতে হয়। এগুলোকেও ActiveRecord জড়িত ক্লাস এবং টেবিল অনুমান করতে ব্যবহার করে।

ActiveRecord ব্যবহার করে users এবং invoices টেবিল কীভাবে ম্যাপ করা হয় সেটা দেখুন:

users

class User < ApplicationRecord
  has_many :invoices
end

invoices

class Invoice < ApplicationRecord
  belongs_to :user
end

Ecto: ডেটা ম্যাপিং এবং অ্যাসোসিয়েশন

অন্যদিকে, Ecto ডেভেলপারকে ডেটা সোর্স এবং এর ফিল্ড সম্পর্কে স্পষ্ট হতে বাধ্য করে। যদিও Ecto-তে একই has_many এবং belongs_to ফিচার আছে, তবে এটিও ডেভেলপারকে অ্যাসোসिएটেড টেবিল এবং সেই টেবিলের স্কিমা হ্যান্ডল করার জন্য ব্যবহৃত স্কিমা মডিউল সম্পর্কে স্পষ্ট হতে বাধ্য করে।

Ecto কীভাবে users এবং invoices টেবিল ম্যাপ করে:

users

defmodule Financex.Accounts.User do
  use Ecto.Schema
  schema "users" do
    field :full_name, :string
    field :email, :string
    has_many :invoices, Financex.Accounts.Invoice
    timestamps()
  end
end

invoices

defmodule Financex.Accounts.Invoice do
  use Ecto.Schema
  schema "invoices" do
    field :payment_method, :string
    field :paid_at, :utc_datetime
    belongs_to :user, Financex.Accounts.User
    timestamps()
  end
end

উপসংহার

এই পোস্টে, আমরা এক মুহূর্তে আপেল এবং কমলাবগানের তুলনা করলাম। আমরা তুলনা করলাম ActiveRecord এবং Ecto কীভাবে ডেটাবেস মাইগ্রেশন এবং ম্যাপিং হ্যান্ডল করে। নিঃশব্দ মূল ব্যাটগার্ল বনাম স্পষ্ট 'আমি ব্যাটম্যান' ব্যাটম্যানের যুদ্ধ।

"কনভেনশন ওভার কনফিগারেশন"-এর ধন্যবাদ, ActiveRecord ব্যবহার করা সাধারণত কম লিখতে হয়। Ecto বিপরীত দিকে যায়, ডেভেলপারদের তাদের উদ্দেশ্য সম্পর্কে আরও স্পষ্ট হতে বাধ্য করে। "কম কোড" সাধারণত ভালো হওয়ার পাশাপাশি, ActiveRecord-এ কিছু অপ্টিমাল ডিফল্ট থাকে যা ডেভেলপারকে সব কিছুতে সিদ্ধান্ত নেওয়া থেকে এবং সকল আন্ডারলাইং কনফিগারেশন বুঝতে হয়ে থাকা থেকে বাঁচায়। শিক্ষার্থীদের জন্য, ActiveRecord একটি আরও উপযুক্ত সমাধান, কারণ এটি "যথেষ্ট ভালো" সিদ্ধান্ত ডিফল্টভাবে নেয় যতক্ষণ না আপনি কঠোরভাবে এর স্ট্যান্ডার্ড অনুসরণ করেন।

Ecto-র স্পষ্ট দিকটি কোডের আচরণ পড়া এবং বোঝা সহজ করে তোলে, কিন্তু এটিও ডেভেলপারকে ডেটাবেস প্রোপার্টি এবং উপলব্ধ ফিচার সম্পর্কে আরও বুঝতে বাধ্য করে। যা প্রথম দৃষ্টিতে Ecto-কে ক্লાંকি দেখাতে পারে, সেটি আসলে এর গুণ। আমার ActiveRecord এবং Ecto উভয়ের পার্সোনাল এক্সপেরিয়েন্স অনুযায়ী, Ecto-র স্পষ্টতা "বিহাইন্ড দ্যা সিন" ইফেক্ট এবং অনিশ্চিততা দূর করে যা ActiveRecord প্রজেক্টে সাধারণ। ডেভেলপার কোডে যা পড়ে, অ্যাপ্লিকেশনে সেটিই ঘটে এবং কোনো ইম্প্লিসিট আচরণ নেই।

কয়েক সপ্তাহ পর দ্বিতীয় ব্লগে, "ActiveRecord vs Ecto" দুই পর্বের সিরিজে, আমরা কভার করব কোয়ারি এবং ভ্যালিডেশন কীভাবে ActiveRecord এবং Ecto-তে কাজ করে।

আমরা জানতে চাই আপনি এই আর্টিকেল সম্পর্কে কী ভাবেন। আমরা সবসময় নতুন টপিক কভার করার জন্য তত্পর, তাই আপনার যদি কোনো বিষয় থাকে যা আপনি আরও জানতে চান, দয়া করে @AppSignal-এ আমাদের জানাতে संকোচ করবেন না!

  1. ডাটাবেসে বাইনারি রিলেশনশিপ

  2. ডাটাবেসে N-ary রিলেশনশিপ: ইউনারি, বাইনারি ও টারনারি

  3. ER ডায়াগ্রাম মিনিমাইজেশন: কার্ডিনালিটি অনুযায়ী টেবিল সংখ্যা কমানো

  4. সমাধান: Err_Connection_Closed