জার্সিভল্ট: React ও Supabase দিয়ে স্কেলেবল ই-কমার্স প্ল্যাটফর্ম তৈরির কারিগরি কেস স্টাডি
React ও Supabase দিয়ে তৈরি হাই-পারফরম্যান্স স্পোর্টস জার্সি ই-কমার্স প্ল্যাটফর্ম।
স্পোর্টস জার্সি ও মার্চেন্ডাইজ বিক্রেতারা সাধারণত ধীরগতির ঐতিহ্যবাহী CMS (যেমন ভারী WooCommerce) ব্যবহার করতে গিয়ে বিপাকে পড়েন। বড় কোনো টুর্নামেন্ট বা ফাইনাল ম্যাচের সময় হঠাৎ ট্রাফিক বাড়লে এই সাইটগুলো ক্র্যাশ করে বা কার্ট লোড হতে অতিরিক্ত সময় নেয়। এছাড়াও জটিল মাইক্রোসার্ভিস আর্কিটেকচার সেটআপ ও মেইনটেইন করার খরচ অনেক বেশি। জার্সিভল্ট প্রজেক্টের লক্ষ্য ছিল একটি বিষয় প্রমাণ করা: লাইটওয়েট React ফ্রন্টএন্ড এবং সার্ভারলেস PostgreSQL ডাটাবেজ (Supabase) ব্যবহার করে কি কোনো ফিক্সড হোস্টিং খরচ ছাড়াই বিদ্যুতগতির স্পোর্টস ই-কমার্স সাইট তৈরি করা সম্ভব?
জার্সিভল্ট: React ও Supabase দিয়ে স্কেলেবল ই-কমার্স প্ল্যাটফর্ম তৈরির কারিগরি কেস স্টাডি
১. প্রজেক্টের পটভূমি ও পরিচিতি
জার্সিভল্ট (Jerseyvault) হলো স্পোর্টস জার্সি ও ফুটবল কিট প্রেমীদের জন্য তৈরি একটি আধুনিক, হেডলেস ই-কমার্স ওয়েব প্ল্যাটফর্ম। React.js, Supabase, PostgreSQL এবং আধুনিক CSS দিয়ে তৈরি এই প্ল্যাটফর্মটি প্রমাণ করে যে কোনো জটিল ও ব্যয়বহুল ইনফ্রাস্ট্রাকচার ছাড়াই কীভাবে একটি দ্রুতগতির ও নির্ভরযোগ্য শপিং এক্সপেরিয়েন্স তৈরি করা যায়।
- ভূমিকা: ফুল-স্ট্যাক আর্কিটেক্ট ও ডেভেলপার
- সময়কাল: ৩ সপ্তাহ
- টেক স্ট্যাক: React, Supabase (PostgreSQL, Auth), Context API, CSS Modules
- লাইভ ওয়েবসাইট: jerseyvault.vercel.app
- গিটহাব রিপোজিটরি: github.com/zahidhasantonmoy/Jerseyvault
২. সমস্যা: ঐতিহ্যবাহী CMS-এর সীমাবদ্ধতা
আমাদের লোকাল মার্কেটে ছোট ও মাঝারি অনলাইন শপগুলো সাধারণত ভারী ওয়ার্ডপ্রেস/কমার্স নির্ভর সলিউশন ব্যবহার করে। সাধারণ কাজের জন্য এগুলো সহজ হলেও কিছু গুরুতর সমস্যা দেখা দেয়:
১. ধীরগতি ও মোবাইল ল্যাগ: অতিরিক্ত প্লাগইনের কারণে পেজের সাইজ ৪-৫ মেগাবাইট ছাড়িয়ে যায়, যা সাধারণ স্মার্টফোনে লোড হতে অনেক বেশি সময় নেয়। ২. হঠাৎ ট্রাফিকে ক্র্যাশ ও স্টক মিসম্যাচ: বড় কোনো খেলার ফাইনাল বা নতুন জার্সি রিলিজের সময় একসঙ্গে অনেক ক্রেতা অর্ডার করতে গেলে সাইট হ্যাং হয়ে যায় এবং অনেক সময় স্টক না থাকলেও অর্ডার সম্পন্ন হয়ে যায়। ৩. কাস্টমাইজেশনের জটিলতা: বিশেষ সাইজ চার্ট বা প্লেয়ার এডিশন ব্যাজের মতো কাস্টম ফিচার যোগ করা কঠিন হয়ে পড়ে।
এই সমস্যাগুলোর কার্যকর সমাধান হিসেবে জার্সিভল্টকে একটি সম্পূর্ণ কাস্টম সিঙ্গেল-পেজ অ্যাপ ও সার্ভারলেস এসকিউএল আর্কিটেকচারে তৈরি করা হয়।
৩. আর্কিটেকচার ও টেকনোলজি নির্বাচন
প্রজেক্টটি শুরুর সময় আমি আর্কিটেকচারাল ফ্লো এমনভাবে সাজাই যাতে ফ্রন্টএন্ড এবং ডাটাবেজ স্বাধীনভাবে কাজ করতে পারে:
┌────────────────────────────────────────────────────────┐
│ React SPA ক্লায়েন্ট │
│ (React Context + Reducer + LocalStorage ক্যাশ) │
└───────────────────────────┬────────────────────────────┘
│ HTTPS / REST API
▼
┌────────────────────────────────────────────────────────┐
│ Supabase লেয়ার │
│ - JWT অথেন্টিকেশন ও সেশন ভ্যালিডেশন │
│ - Row Level Security (RLS) ইঞ্জিন │
└───────────────────────────┬────────────────────────────┘
│ SQL ট্রানজেকশন
▼
┌────────────────────────────────────────────────────────┐
│ PostgreSQL রিলেশনাল ডাটাবেজ │
│ (প্রোডাক্ট, অর্ডার, ইনভেন্টরি, অ্যাটমিক ফাংশন) │
└───────────────────────────┬────────────────────────────┘
কেন MongoDB-এর বদলে Supabase ও PostgreSQL?
- ডাটা ইন্টিগ্রিটি: ই-কমার্সে ACID প্রোপার্টি অত্যন্ত জরুরি। PostgreSQL-এর ফরেন কি এবং চেক কনস্ট্রেইন্ট নিশ্চিত করে যে ডাটাবেজে কখনো অসম্পূর্ণ বা ইনভ্যালিড অর্ডার এন্ট্রি হতে পারবে না।
- দ্রুত ব্যাকএন্ড রেডি: আলাদা Node/Express সার্ভার লিখে প্রতিটি এন্ডপয়েন্ট বানানোর বদলে Supabase সরাসরি ডাটাবেজ টেবিল থেকে টাইপ-সেফ এপিআই তৈরি করে দেয়, যা প্রচুর সময় বাঁচায়।
৪. মূল কারিগরি হাইলাইটস
ক. Row Level Security (RLS)
ডাটাবেজ লেভেলেই ইউজারের নিরাপত্তা লক করে দেওয়া হয়েছে। ফলে কোনো ভিজিটর চাইলেও অন্য কারো অর্ডার দেখতে পারবে না:
-- শুধুমাত্র নিজের অর্ডার দেখার অনুমতি পলিসি
CREATE POLICY "Users can view own orders"
ON public.orders
FOR SELECT
USING (auth.uid() = user_id);
খ. অপটিমিস্টিক কার্ট আপডেট
ব্যবহারকারীর অভিজ্ঞতায় যাতে এক মুহূর্তের জন্যও কোনো ল্যাগ অনুভূত না হয়, সেজন্য কার্টে কিছু যোগ করার সাথে সাথে UI আপডেট হয়ে যায় এবং ব্রাউজার মেমোরিতে স্টেট ক্যাশ হয়ে যায়:
// অপটিমিস্টিক কার্ট মিউটেশন লজিক
case 'ADD_TO_CART': {
const existingIndex = state.items.findIndex(i => i.id === action.payload.id && i.size === action.payload.size);
let updated;
if (existingIndex > -1) {
updated = [...state.items];
updated[existingIndex].quantity += action.payload.quantity;
} else {
updated = [...state.items, action.payload];
}
localStorage.setItem('jv_cart', JSON.stringify(updated));
return { ...state, items: updated };
}
৫. আসল চ্যালেঞ্জ: একই সময়ে একাধিক অর্ডারে স্টক সংক্রান্ত সংঘাত
সমস্যাটি কী ছিল?
টেস্টিংয়ের সময় দেখা গেল, যখন শেষ একটি জার্সি স্টকে ছিল এবং দুজন ব্যবহারকারী একই সেকেন্ডে "অর্ডার করুন" চাপল, তখন দুজনের অর্ডারই সম্পন্ন হয়ে গেল এবং স্টকের সংখ্যা শূন্যের নিচে (-১) চলে গেল।
যেভাবে সমাধান করা হলো
ক্লায়েন্ট-সাইড লজিক বাদ দিয়ে আমি সরাসরি PostgreSQL-এর ভেতরে একটি Stored Procedure তৈরি করি। সেখানে SELECT ... FOR UPDATE ব্যবহার করে রো লক কার্যকর করা হয়:
CREATE OR REPLACE FUNCTION process_checkout(p_product_id UUID, p_quantity INT)
RETURNS BOOLEAN AS $$
DECLARE
current_stock INT;
BEGIN
SELECT stock INTO current_stock FROM products WHERE id = p_product_id FOR UPDATE;
IF current_stock < p_quantity THEN
RAISE EXCEPTION 'পর্যাপ্ত স্টক নেই';
END IF;
UPDATE products SET stock = stock - p_quantity WHERE id = p_product_id;
RETURN TRUE;
END;
$$ LANGUAGE plpgsql;
একটি ট্রানজেকশন সফলভাবে শেষ না হওয়া পর্যন্ত ডাটাবেজ অন্য কোনো রিকোয়েস্টকে ওই রো স্পর্শ করতে দেয় না। ফলে রেস কন্ডিশনের ঝুঁকি চিরতরে দূর হয়ে যায়।
৬. বাস্তব ফলাফল ও অর্জন
- ১০০% নির্ভুল ইনভেন্টরি: কনকারেন্ট টেস্টে একটি ভুল বা ওভার-সেলিং অর্ডারও রেকর্ড হয়নি।
- বিদ্যুতগতির নেভিগেশন: প্রতিটি পেজ ট্রানজিশন ১ সেকেন্ডের কম সময়ে সম্পন্ন হয়।
- শূন্য হোস্টিং খরচ: ভার্সেল ও সুপাবেজের ফ্রি টিয়ারেই পুরো আর্কিটেকচার চলছে, যা প্রাথমিক ব্যবসার জন্য কোনো আর্থিক বোঝা তৈরি করে না।
৭. আপনার বিজনেসের জন্য কাস্টম ওয়েব অ্যাপ বানাতে চান?
আপনার যদি কোনো কাস্টম ই-কমার্স প্ল্যাটফর্ম, হাই-পারফরম্যান্স ওয়েব অ্যাপ্লিকেশন বা নির্ভরযোগ্য ডাটাবেজ আর্কিটেকচার প্রয়োজন হয়, তবে আমার সার্ভিসেস পেজে যোগাযোগ করতে পারেন।
সবচেয়ে বড় জটিলতা তৈরি হয়েছিল মাল্টি-ভেরিয়েন্ট ইনভেন্টরি ম্যানেজমেন্টের সময় (একই জার্সির ভিন্ন ভিন্ন সাইজ ও স্টক)। প্রথমে ফ্রন্টএন্ড থেকে স্টক চেক করে অর্ডার প্লেস করা হচ্ছিল। কিন্তু পরীক্ষার সময় দেখা গেল, দুজন ভিজিটর একই সময়ে শেষ একটি জার্সিতে ক্লিক করলে দুজনেরই অর্ডার সাবমিট হয়ে ডাটাবেজে স্টক মাইনাস (-১) হয়ে যাচ্ছে। *প্রথম ব্যর্থ চেষ্টা:* চেকআউট ফর্মে আসার পর ক্লায়েন্ট-সাইড স্টেট দিয়ে পুনরায় স্টক চেক করা। কিন্তু ব্যবহারকারী ঠিকানা লিখতে লিখতে অন্য কেউ কিনে ফেললে ভুল থেকেই যাচ্ছিল। *কার্যকর সমাধান:* আমি পুরো ইনভেন্টরি ডিডাকশন লজিক ডাটাবেজের ভেতরে একটি PostgreSQL Stored Function (`process_checkout`) হিসেবে স্থানান্তর করি। সেখানে `SELECT ... FOR UPDATE` ব্যবহার করে রো-লেভেল লক বসানো হয়। ফলে একটি ট্রানজেকশন শেষ না হওয়া পর্যন্ত ডাটাবেজ অন্য কাউকে স্টক কমাতে দেয় না এবং স্টক শেষ হলে সরাসরি এরর থ্রো করে।
প্রজেক্টটি সফলভাবে ভার্সেলে ডিপ্লয় করা হয়েছে: - ফোর-জি মোবাইল কানেকশনেও ১.২ সেকেন্ডের কম সময়ে পেজ লোড সম্পন্ন হয়। - ২০টি অটোমেটেড কনকারেন্ট টেস্টে কোনো ডুপ্লিকেট অর্ডার বা ইনভেন্টরি মিসম্যাচ ঘটেনি। - সম্পূর্ণ ফ্রি-টিয়ার ইনফ্রাস্ট্রাকচারে চলমান, অর্থাৎ প্রতি মাসে হোস্টিং বা সার্ভার ফি $০। - এটি যেকোনো কাস্টম হেডলেস ই-কমার্স ডেভেলপমেন্টের জন্য একটি সলিড প্রোডাকশন রেফারেন্স হিসেবে কাজ করছে।
আপনার প্রজেক্টের জন্য একই মানের আর্কিটেকচার চান?
ফুল-স্ট্যাক ওয়েব অ্যাপ্লিকেশন, এআই এজেন্ট বা কাস্টম ব্যাকএন্ড — আপনার প্রজেক্টের রিকোয়ারমেন্ট নিয়ে বিস্তারিত আলোচনা করা যাক।
হায়ার করুন ও প্রজেক্ট শুরু করুন