লোকাল ক্যাশ, পুরনো ডেটা এবং রিফ্রেশ নিয়ম নিয়ে Flutter ক্যাশিং কৌশল: কী স্টোর করবেন, কখন অবৈধ করবেন, এবং স্ক্রিনগুলো কীভাবে সামঞ্জস্যপূর্ণ রাখবেন।

মোবাইল অ্যাপে ক্যাশিং মানে ডেটার একটি কপি কাছেই রাখা (মেমরি বা ডিভাইসে), যাতে পরের স্ক্রীন নেটওয়ার্কের অপেক্ষা না করে মুহূর্তেই রেন্ডার করতে পারে। সেই ডেটা হতে পারে আইটেমের লিস্ট, একটি ইউজার প্রোফাইল, বা সার্চ রেজাল্ট।
কঠিন অংশ হলো: ক্যাশকৃত ডেটা প্রায়ই একটু ভুল থাকে। ব্যবহারকারী দ্রুত টের পায়: দাম আপডেট হয়নি, ব্যাজ কাউন্ট আটকে আছে, বা ডিটেইল স্ক্রিনে ঠিকই দেখাচ্ছে পুরনো তথ্য ঠিক তখনই যখন তারা সেটি পরিবর্তন করেছে। এই সমস্যার ডিবাগিংকে কঠিন করে তোলে টাইমিং: একই এন্ডপয়েন্ট pull-to-refresh-এ ঠিক থাকতে পারে, কিন্তু ব্যাক নেভিগেশন, অ্যাপ রিসিউম বা অ্যাকাউন্ট বদলের পরে ভুল দেখাতে পারে।
একটা বাস্তব ট্রেড-অফ আছে। সবসময় নতুন ডেটা ফেচ করলে স্ক্রিন ধীর এবং ঝাঁপিং মনে হয়, ব্যাটারি ও ডাটা বেশি খরচ হয়। যদি খুব আগ্রাসিভলি ক্যাশ করা হয়, অ্যাপ দ্রুত লাগে, কিন্তু মানুষ দেখানো তথ্যের উপর বিশ্বাস হারায়।
একটি সহজ লক্ষ্য সহায়ক: সতেজতা (freshness) ভব্যমানযোগ্য করুন। প্রতিটি স্ক্রিন কী দেখাতে পারবে (তাজা, সামান্য পুরনো, না অফলাইন), ডেটা কতক্ষণ থাকতে পারে তার নিয়ম, এবং কোন ইভেন্টগুলো এটি ইনভ্যালিড করবে—এসব ঠিক করুন।
একটি সাধারণ ফ্লো ভাবুন: একজন ব্যবহারকারী অর্ডার খোলে, তারপর অর্ডার লিস্টে ফিরে যায়। যদি লিস্ট ক্যাশ থেকে আসে, এটি পুরনো স্ট্যাটাস দেখাতে পারে। আপনি যদি প্রতিবার রিফ্রেশ করেন, লিস্ট ফ্লিকার করে ধীর মনে হতে পারে। স্পষ্ট নিয়ম — “ক্যাশ করা মাত্রি তৎক্ষণাৎ দেখান, পেছনে রিফ্রেশ চালান, এবং রেসপন্স এলে উভয় স্ক্রিন আপডেট করুন” — নেভিগেশনের জুড়ে অভিজ্ঞতাকে সামঞ্জস্যপূর্ণ করে।
ক্যাশ শুধু "সংরক্ষিত ডেটা" নয়। এটা একটি কপি এবং সেই কপি কতদিন পর্যন্ত বৈধ তার নিয়ম। যদি আপনি পে-লোড স্টোর করে রাখেন কিন্তু নিয়ম না রাখেন, তখন বাস্তবে দুইটি বাস্তবতার মধ্যে পড়বেন: একটি স্ক্রিন নতুন দেখায়, অন্যটি আগের দিনের।
প্রায়োগিকভাবে প্রতিটি ক্যাশ আইটেম তিনটি অবস্থার একটি তে রাখুন:
এভাবে UI প্রতিবার সেই অবস্থার সম্মুখীন হলে একইভাবে সাড়া দিতে পারে, ফলে ব্যবহারকারীর অভিজ্ঞতা ভব্যমানযোগ্য হয়।
ফ্রেশনেস নিয়মগুলো এমন সিগন্যালের উপর ভিত্তি করে হওয়া উচিত যা সহকর্মীর কাছে সহজে ব্যাখ্যা করা যায়। সাধারণ পছন্দগুলো: টাইম-ভিত্তিক এক্সপায়ারি (যেমন ৫ মিনিট), ভার্সন চেঞ্জ (স্কিমা বা অ্যাপ ভার্সন), ব্যবহারকারীর অ্যাকশন (pull-to-refresh, submit, delete), অথবা সার্ভার-হিন্ট (ETag, last-updated timestamp, বা স্পষ্ট “cache invalid” রেসপন্স)।
উদাহরণ: একটি প্রোফাইল স্ক্রিন ক্যাশ করা ইউজার ডেটা তৎক্ষণাৎ লোড করে। যদি এটা পুরনো কিন্তু ব্যবহারযোগ্য হয়, কপি নাম ও অ্যাভাটার দেখায় এবং তারপর চুপচাপ রিফ্রেশ করে। যদি ইউজার সম্প্রতি প্রোফাইল এডিট করে, সেটি একটা রিফ্রেশ আবশ্যক মুহূর্ত — অ্যাপকে ক্যাশ তৎক্ষণাৎ আপডেট করে সব স্ক্রিনকে সামঞ্জস্যপূর্ণ রাখতে হবে।
এই নিয়মগুলোর মালিক কে তা ঠিক করাও জরুরি। বেশিরভাগ অ্যাপে ভাল ডিফল্ট: ডেটা লেয়ার ফ্রেশনেস ও ইনভ্যালিডেশন নিয়ন্ত্রণ করে, UI কেবল প্রতিক্রিয়া জানায় (ক্যাশ দেখানো, লোডিং দেখানো, এরর দেখানো), এবং ব্যাকএন্ড যতটা পারে হিন্ট দেয়। এতে প্রতিটি স্ক্রিন আলাদা নিয়ম বানাতে পারে না।
ভাল ক্যাশিং শুরু হয় একটি প্রশ্ন নিয়ে: যদি এই ডেটা একটু পুরনো হয়, ব্যবহারকারীর ক্ষতি হবে কি? যদি উত্তর “সম্ভবত ঠিক আছে” হয়, সাধারণত লোকাল ক্যাশিং উপযুক্ত।
অত্যধিক পড়া হয় এবং ধীরে পরিবর্তনশীল ডেটা সাধারণত ক্যাশ করার যোগ্য: ফিড ও লিস্ট যেগুলো স্ক্রল করা হয়, ক্যাটালগ-শৈলীর কনটেন্ট (প্রোডাক্ট, আর্টিকেল, টেমপ্লেট), এবং রেফারেন্স ডেটা যেমন ক্যাটাগরি বা দেশ। সেটিংস ও পছন্দসই আইটেমও এখানে রাখা যায়, সাথে বেসিক প্রোফাইল তথ্য (নাম, অ্যাভাটার URL)।
ঝুঁকিপূর্ণ ভাগ হলো যেকোনো মনি-রিলেটেড বা টাইম-ক্রিটিক্যাল ডেটা। ব্যালান্স, পেমেন্ট স্ট্যাটাস, স্টক অ্যাভেইলেবিলিটি, অ্যাপয়েন্টমেন্ট স্লট, ডেলিভারি ETA, এবং “শেষে অনলাইন” টাইপের তথ্য পুরনো হলে প্রকৃত সমস্যা ঘটতে পারে। এগুলো স্পিডের জন্য ক্যাশ করা যেতে পারে, কিন্তু সিদ্ধান্তের আগে (যেমন অর্ডার নিশ্চিত করার আগে) ক্যাশকে শুধু একটি অস্থায়ী প্লেসহোল্ডার ধরে ফোর্স রিফ্রেশ করুন।
ডেরাইভড UI স্টেট আলাদা ক্যাটাগরি: নির্বাচিত ট্যাব, ফিল্টার, সার্চ কুয়েরি, সর্ট অর্ডার, বা স্ক্রল পজিশন সেভ করলে নেভিগেশন স্মুথ লাগে। কিছুক্ষেত্রে পুরনো পছন্দগুলি অপ্রত্যাশিতভাবে ফিরে আসলে মানুষ বিভ্রান্ত হতে পারে। সহজ নিয়ম: ব্যবহারকারী ওই ফ্লোতে থাকাকালীন UI স্টেট মেমরিতে রাখুন, কিন্তু যখন তারা ইচ্ছাকৃতভাবে “শুরু থেকে” যায় (যেমন হোমে ফিরে) তখন রিসেট করুন।
সিকিউরিটি বা প্রাইভেসি ঝুঁকি তৈরি করে এমন ডেটা ক্যাশ করা এড়িয়ে চলুন: সিক্রেটস (পাসওয়ার্ড, API কী), ওয়ান-টাইম টোকেন (OTP, পাসওয়ার্ড রিসেট টোকেন), এবং সংবেদনশীল ব্যক্তিগত ডেটা যদি না অফলাইনের জন্য সত্যিই প্রয়োজন হয়। কখনোই ফুল কার্ড ডিটেইলস বা এমন কিছু ক্যাশ করবেন না যা ফ্রড বাড়ায়।
শপিং অ্যাপে প্রোডাক্ট লিস্ট ক্যাশ করা বড় বিজয়। কিন্তু চেকআউট স্ক্রিনে টোটাল ও অ্যাভেইলেবিলিটি নিশ্চিত করতে সর্বদা শেষ মুহূর্তে রিফ্রেশ করুন।
অধিকাংশ Flutter অ্যাপে লোকাল ক্যাশ প্রয়োজন যাতে স্ক্রিন দ্রুত লোড হয় এবং নেটওয়ার্ক জাগার সময় ফ্ল্যাশ না করে। গুরুত্বপূর্ণ সিদ্ধান্ত হলো ক্যাশ কোন লেয়ারে থাকবে—প্রতিটি লেয়ারের গতি, সাইজ সীমা, এবং ক্লিনআপ আচরণ ভিন্ন।
মেমরি ক্যাশ সবচেয়ে দ্রুত। এটি সেশন চলাকালীন পুনঃব্যবহারের জন্য দারুণ: বর্তমান ইউজার প্রোফাইল, শেষ সার্চ রেজাল্ট, বা ব্যবহারকারী শোনা প্রোডাক্ট। ট্রেডঅফ: অ্যাপ kill হলে এটি লোপ পায়, তাই cold start বা অফলাইনে সহায়তা করবে না।
ডিস্ক কী-ভ্যালু স্টোরেজ ছোট আইটেমের জন্য ভালো যা রিস্টার্টের পরও থাকা উচিত: প্রেফারেন্স, “শেষ নির্বাচিত ট্যাব”, এবং ছোট JSON রেসপন্স যেগুলো অনিবার্যভাবে কম পরিবর্তিত। ইচ্ছাকৃত ভাবেই ছোট রাখুন—বড় লিস্ট কী-ভ্যালু-তে ফেলার পর আপডেট জটিল হয়ে যায় এবং ব্লোট বাড়ে।
লোকাল ডাটাবেস সেরা যখন আপনার ডেটা বড়, স্ট্রাকচার্ড, বা অফলাইন ব্যবহার প্রয়োজন। কোয়েরি দরকার হলে ("সব আনরিড মেসেজ", "কার্টের আইটেম", "গত মাসের অর্ডার") ডাটাবেস সুবিধা দেয়—একটু বড় ব্লব লোড করে মেমরিতে ফিল্টার করার চেয়ে।
ক্যাশিং ভব্যমানযোগ্য রাখতে প্রতিটি ডেটা টাইপের জন্য এক প্রাইমারি স্টোর বাছুন এবং একই ডেটাসেটে তিন জায়গায় কপি রাখার থেকে বিরত থাকুন।
দ্রুত রুল অফ থাম:
সাইজও পরিকল্পনা করুন। “অনেক বড়” কি বোঝায়, কতো দিন রাখবেন, এবং কিভাবে ক্লিনআপ করবেন—উদাহরণ: cached search results শেষ ২০ কুয়েরি পর্যন্ত ক্যাপ করুন, এবং ৩০ দিনের পুরনো রেকর্ডগুলো নিয়মিত রিমুভ করুন যাতে ক্যাশ চুপচাপ বাড়ে না।
রিফ্রেশ নিয়মগুলো সহজ হওয়া উচিত—প্রতিটি স্ক্রিনের জন্য আপনি এক বাক্যে ব্যাখ্যা করতে পারবেন। তাতেই সংবেদনশীল ক্যাশিং থেকে সুবিধা আসে: ব্যবহারকারী দ্রুত স্ক্রিন পায় এবং অ্যাপ নির্ভরযোগ্য থাকে।
সবচেয়ে সহজ নিয়ম হলো TTL (time to live)। ডেটাকে একটি টাইমস্ট্যাম্প দিয়ে সংরক্ষণ করুন এবং ধরে নিন, উদাহরণস্বরূপ, ৫ মিনিট পর্যন্ত তা তাজা। এর পর এটি স্টেইল হয়ে যায়। TTL ফিড, ক্যাটাগরি, বা রিকমেন্ডেশনের মতো “ভালো হলে ভাল” ডেটার জন্য ভালো।
সহায়ক পরিমার্জন হলো soft TTL ও hard TTL আলাদা করা।
soft TTL-এ আপনি ক্যাশ ডেটা তৎক্ষণাৎ দেখান, তারপর পেছনে রিফ্রেশ করে UI আপডেট করেন যদি পরিবর্তন ঘটে। hard TTL-এ একবার সময় শেষ হলে পুরনো ডেটা দেখানো বন্ধ করে দেন—আপনি লোডার দেখান বা “অফলাইন/পুনরায় চেষ্টা করুন” স্টেট। hard TTL এমন কেসে যা ভুল হলে ধীরতা ভালতর (যেমন ব্যালান্স, অর্ডার স্ট্যাটাস, পারমিশন)।
আপনার ব্যাকএন্ড যদি সমর্থন করে, “শুধু পরিবর্তন হলে রিফ্রেশ” পছন্দ করুন—ETag, updatedAt বা version ফিল্ড ব্যবহার করে। অ্যাপ জিজ্ঞাসা করতে পারে “এটি পরিবর্তন হয়েছে কি?” এবং কিছু না থাকলে পুরো পে-লোড ডাউনলোড করা এড়াবে।
বহু স্ক্রিনের জন্য ব্যবহারকারী-বান্ধব ডিফল্ট হলো stale-while-revalidate: এখনই দেখান, চুপচাপ রিফ্রেশ করুন, এবং ফলাফল ভিন্ন হলে পুনরায় ড্র করে দিন। এতে গতি মিলবে এবং র্যান্ডম ফ্লিকার কম হবে।
প্রতি-স্ক্রিন ফ্রেশনেস প্রায়ই এমন হয়:
ফ্রেশনেসের নিয়ম বেছে নিন সেই ভিত্তিতে যে ভুল হলে খরচ কত—শুধু ফেচিং-এর খরচ নয়।
ক্যাশ ইনভ্যালিডেশন শুরু হয় একটি প্রশ্ন দিয়ে: কোন ইভেন্ট ক্যাশকে অপর্যাপ্ত করে তোলে যাতে রিফেচ করার খরচ তখনও যুক্তিযুক্ত? যদি আপনি কয়েকটি ট্রিগার বেছে নিয়ে সেগুলোতে কড়া থাকেন, আচরণ ভব্যমানযোগ্য থাকবে এবং UI স্থির মনে হবে।
বাস্তব অ্যাপে সবচেয়ে গুরুত্বপূর্ণ ট্রিগারগুলো:
উদাহরণ: ইউজার প্রোফাইল ফটো এডিট করে, তারপর ফিরে গেলে যদি আপনি শুধু টাইম-ভিত্তিক রিফ্রেশের উপর নির্ভর করেন, পূর্বের স্ক্রিন পুরনো ইমেজ দেখাবে যতক্ষণ না পরবর্তী ফেচ। বদলে এডিটকে ট্রিগার হিসেবে নিন: লোকালি প্রোফাইল অবজেক্ট আপডেট করে নতুন savedAt টাইমস্ট্যাম্প দিন।
ইনভ্যালিডেশন নিয়মগুলো ছোট ও স্পষ্ট রাখুন। যদি আপনি ঠিক কোন ইভেন্ট ক্যাশ এন্ট্রি ইনভ্যালিড করে তা নামতে না পারেন, আপনি হয় অনেক বেশি রিফ্রেশ করবেন (ধীর, ঝাঁপন্ত UI) বা যথেষ্টভাবে রিফ্রেশ করবেন না (স্টেইল স্ক্রিন)।
প্রথমে আপনার মূল স্ক্রিনগুলো এবং প্রত্যেকটির জন্য প্রয়োজনীয় ডেটা তালিকাভুক্ত করুন। এন্ডপয়েন্টে ভাববেন না, ব্যবহারকারী-দৃশ্যমান অবজেক্টে ভাবুন: প্রোফাইল, কার্ট, অর্ডার লিস্ট, ক্যাটালগ আইটেম, আনরিড কাউন্ট।
পরবর্তী ধাপ: প্রতি ডেটা টাইপের জন্য এক source of truth বাছুন। Flutter-এ এটি সাধারণত একটি repository যা ডেটা কোথা থেকে আসে (মেমরি, ডিস্ক, নেটওয়ার্ক) লুকায়। স্ক্রিনগুলো নেটওয়ার্ক কখন টেনাক করবে তা নির্ধারণ করবেন না—তারা repository থেকে ডেটা চাইবে এবং ফেরত দেয়া স্টেটে প্রতিক্রিয়া জানাবে।
একটি ব্যবহারিক ফ্লো:
মেটাডেটা হলো যা নিয়মগুলো কার্যকর করে। যদি ownerUserId বদলে যায় (লগআউট/লগইন), আপনি তৎক্ষণাত পুরোনো ক্যাশ রোগুলো drop বা ignore করতে পারবেন যাতে আগের ইউজারের ডেটা সাময়িকভাবে দেখায় না।
UI আচরণ নির্ধারণ করুন আগে থেকেই যে “স্টেইল” কী মানে। একটা প্রচলিত নিয়ম: স্টেইল ডেটা তৎক্ষণাৎ দেখান যাতে স্ক্রিন ব্ল্যাঙ্ক না হয়, পেছনে রিফ্রেশ চালান, এবং নতুন ডেটা এলে আপডেট করুন। যদি রিফ্রেশ ব্যর্থ হয়, স্টেইল ডেটা রাখুন এবং একটি ছোট, পরিষ্কার এরর দেখান।
তারপর নিয়মগুলোকে কয়েকটি টেস্ট দিয়ে লক করুন:
এইটাই “আমাদের কাছে ক্যাশ আছে” আর “আমাদের অ্যাপ প্রতিবার একইভাবে আচরণ করে”—এর মধ্যে পার্থক্য।
কিছুই বিশ্বাস ভঙ্গ করে তুলতে পারেনা যেমন: একটি লিস্ট স্ক্রিনে একটি মান দেখানো, ডিটেইলে গিয়ে তা সম্পাদনা করা, তারপর ফিরে গেলে পুরনো মান দেখা। নেভিগেশনের জুড়ে সামঞ্জস্য আসে যখন প্রতিটি স্ক্রিন একই সোর্স থেকে পড়ে।
একটি শক্ত নিয়ম: একবার ফেচ করুন, একবার সংরক্ষণ করুন, বহুবার রেন্ডার করুন। স্ক্রিনগুলো একই এন্ডপয়েন্ট আলাদাভাবে কল করে নিজস্ব কপি রাখলে সমস্যা হয়। শেয়ার্ড স্টোর (আপনার স্টেট ম্যানেজমেন্ট লেয়ার) ব্যবহার করুন, এবং লিস্ট ও ডিটেইল উভয়ই ঐ একই ডেটা দেখুক।
একটি জায়গাই বর্তমান মান ও ফ্রেশনেসের মালিক হবে। স্ক্রিন রিফ্রেশ অনুরোধ করতে পারবে, কিন্ত তাদের টিমার, রিট্রাই বা পার্সিং আলাদাভাবে পরিচালনা করা উচিত নয়।
প্রায়োগিক অভ্যাস যা “দুই বাস্তবতার সংস্করণ” রোধ করে:
ভাল নিয়ম থাকলেও ব্যবহারকারী কখনো কখনো স্টেইল ডেটা দেখবে (অফলাইন, ধীর নেটওয়ার্ক, ব্যাকগ্রাউন্ডেড অ্যাপ)। তা স্পষ্ট করুন ছোট, শান্ত সিগন্যাল দিয়ে: “আপডেট হয়েছে: কিছুক্ষণের মধ্যে” টাইমস্ট্যাম্প, সূক্ষ্ম “Refreshing…” ইন্ডিকেটর, বা “Offline” ব্যাজ।
এডিটগুলোর জন্য optimistic updates প্রায়ই ভাল লাগে। উদাহরণ: ব্যবহারকারী ডিটেইলে প্রোডাক্ট প্রাইস পরিবর্তন করে। শেয়ার্ড স্টোর তৎক্ষণাৎ আপডেট করুন যাতে ব্যাক করলে লিস্টে নতুন দাম দেখায়। যদি সেভ ব্যর্থ হয়, আগের মানে রোলব্যাক করে ছোট একটি এরর দেখান।
অধিকাংশ ক্যাশিং ব্যর্থতা সাধারণ: ক্যাশ কাজ করে, কিন্তু কেউ বলতে পারে না কখন তা ব্যবহার করা উচিত, কখন মেয়াদোত্তীর্ণ হবে, এবং কে তার মালিক।
প্রথম ফাঁদ হলো মেটাডেটা ছাড়া ক্যাশ করা। যদি আপনি কেবল পে-লোড সংরক্ষণ করেন, তখন বলতে পারবেন না এটা পুরনো কি না, কোন অ্যাপ/স্কিমা ভার্সন তৈরি করেছিল, বা কোন ইউজারের জন্য—কমপক্ষে savedAt, একটি ভার্সন নম্বর, এবং userId সংরক্ষণ করুন। এটা অনেক “এই স্ক্রিনটি কেন ভুল?” বাগ থেকে রক্ষা করে।
আরেকটি সাধারণ সমস্যা হলো একই ডেটার অনেক ক্যাশ যেখানে কোন নির্ধারিত মালিক নেই। একটি লিস্ট স্ক্রিন মেমরিতে লিস্ট রাখে, repository ডিস্কে লেখে, আর ডিটেইল স্ক্রিন আলাদাভাবে ফেচ করে ভিন্ন জায়গায় রাখে—এটা সমস্যা। একটি সোর্স অফ ট্রুথ বেছে নিন (প্রায়ই repository লেয়ার) এবং প্রতিটি স্ক্রিন সেখানে থেকে পড়ুক।
অ্যাকাউন্ট পরিবর্তন একটি বারবারের ফাঁদ। কেউ যদি লগআউট করে বা অ্যাকাউন্ট বদলায়, ইউজার-স্কোপড টেবিল ও কী মুছুন; নতুবা সাময়িকভাবে আগের ইউজারের প্রোফাইল ছবি বা অর্ডার দেখা যাবে—যা প্রাইভেসি লঙ্ঘন মনে হবে।
প্রায়োগিক ফিক্সগুলি:
উদাহরণ: আপনার প্রোডাক্ট লিস্ট ক্যাশ থেকে তৎক্ষণাৎ লোড হয়, তারপর চুপচাপ রিফ্রেশ করে। যদি রিফ্রেশ ব্যর্থ হয়, ক্যাশ করা ডেটা দেখতেই থাকুক কিন্তু ব্যবহারকারীকে জানিয়ে দিন এটি পুরনো হতে পারে এবং রিট্রাই অফার করুন। রিফ্রেশে ক্যাশ থাকলে UI ব্লক করবেন না।
রিলিজের আগে ক্যাশিংকে “ঠিক আছে মনে হচ্ছে” থেকে এমন নিয়মে পরিণত করুন যা টেস্ট করা যায়। ব্যবহারকারীকে ডেটা মানানসই চোখে দেখাতে হবে এমন পরিস্থিতিতেও—নেভিগেশন, অফলাইন, বা ভিন্ন অ্যাকাউন্টে সাইন-ইন করার পরে।
প্রতিটি স্ক্রিনের জন্য ঠিক করুন ডেটা কতক্ষণ তাজা থাকতে পারে। দ্রুত পরিবর্তনশীল ডেটার জন্য মিনিট, ধীর পরিবর্তনশীলের জন্য ঘণ্টা। তারপর নিশ্চিত করুন স্টেইল হলে কী হবে: ব্যাকগ্রাউন্ড রিফ্রেশ, ওপেন-এ রিফ্রেশ, না ম্যানুয়াল পুল-টু-রিফ্রেশ।
প্রতি ডেটা টাইপের জন্য ঠিক করুন কোন ইভেন্টগুলো ক্যাশ মুছে ফেলবে বা বাইপাস করবে: সাধারণ ট্রিগারগুলোর মধ্যে logout, আইটেম এডিট, অ্যাকাউন্ট সুইচ, এবং ডেটা শেইপ বদলানো অ্যাপ আপডেট আছে।
সুনিশ্চিত করুন cached এন্ট্রিগুলো পে-লোডের পাশে এই মেটাডেটা সংরক্ষণ করে:
মালিকানাও স্পষ্ট রাখুন: প্রতিটি ডেটা টাইপের জন্য একটি repository (উদাহরণ: ProductsRepository) ব্যবহার করুন, না widget-প্রতি। উইজেটগুলো ডেটা চাওয়া ছাড়া ক্যাশ নিয়ম নির্ধারণ করবে না।
ওফলাইন আচরণও নির্ধারণ করে টেস্ট করুন। নিশ্চিত করুন কোন স্ক্রিন ক্যাশ থেকে কি দেখায়, কোন অ্যাকশন ডিসেবল থাকবে, এবং আপনি কী কপি দেখাবেন ("সংরক্ষিত ডেটা দেখানো হচ্ছে" এবং একটি দৃশ্যমান রিফ্রেশ কন্ট্রোল)। প্রতি ক্যাশ-ব্যাকড স্ক্রিনে ম্যানুয়াল রিফ্রেশ থাকা উচিত এবং সহজে পাওয়া যায়।
ধরা যাক একটি সরল শপ অ্যাপ: প্রোডাক্ট ক্যাটালগ (লিস্ট), প্রোডাক্ট ডিটেইলস, এবং একটি ফেভারিট ট্যাব। ব্যবহারকারী ক্যাটালগ স্ক্রল করে, প্রোডাক্ট খুলে, এবং হার্ট ট্যাপে ফেভারিট করে। উদ্দেশ্য: ধীর নেটওয়ার্কেও দ্রুত মনে হওয়া, কনফিউজিং মিলিসম্পাদন ছাড়া।
লোকালি ক্যাশ করুন যা রেন্ডার করতে সাহায্য করে: ক্যাটালগ পেজ (ID, শিরোনাম, দাম, থাম্বনেইল URL, ফেভারিট ফ্ল্যাগ), প্রোডাক্ট ডিটেইলস (ডিসক্রিপশন, স্পেসিফিকেশন, অ্যাভেইলেবিলিটি, lastUpdated), ইমেজ মেটাডেটা (URL, সাইজ, ক্যাশ কী), এবং ইউজারের ফেভারিটস (প্রোডাক্ট ID সেট, বিকল্পভাবে টাইমস্ট্যাম্প)।
যখন ব্যবহারকারী ক্যাটালগ খুলে, ক্যাশ করা রেজাল্ট তৎক্ষণাৎ দেখান, তারপর পেছনে revalidate করুন। যদি ফ্রেশ ডেটা আসে, শুধুমাত্র যে অংশ পরিবর্তিত হয়েছে সেগুলো আপডেট করুন এবং স্ক্রল পজিশন স্থিত রাখুন।
ফেভারিট টগলকে "সামঞ্জস্য জরুরি" অ্যাকশন হিসেবে নিন। লোকালি ফেভারিট সেট তৎক্ষণাৎ আপডেট করুন (optimistic), তারপর সংশ্লিষ্ট ক্যাশড প্রোডাক্ট রো এবং প্রোডাক্ট ডিটেইলস আপডেট করুন। যদি নেটওয়ার্ক কল ব্যর্থ হয়, রোলব্যাক করে একটি ছোট বার্তা দেখান।
নেভিগেশনের সামঞ্জস্য রাখার জন্য লিস্ট ব্যাজ এবং ডিটেইল হার্ট আইকন একই সোর্স অফ ট্রুথ (লোকাল ক্যাশ/স্টোর) থেকে ড্রাইভ করুন—নাহলে পৃথক স্টেট অপেক্ষা করার দরকার পড়বে। লিস্ট হার্ট ফিরে আসার পরে তৎক্ষণাৎ আপডেট হবে, ডিটেইল স্ক্রিন লিস্ট থেকে করা পরিবর্তন মেনে নেবে, এবং ফেভারিট ট্যাব কাউন্ট সব জায়গায় মিলবে।
সহজ রিফ্রেশ নিয়ম যোগ করুন: ক্যাটালগ দ্রুত মেয়াদ শেষ করে (মিনিট), প্রোডাক্ট ডিটেইলস একটু বেশি, এবং ফেভারিটস কখনো এক্সপায়ার না করে কিন্তু লগইন/লগআউটের পরে রিকনসিল করা হয়।
ক্যাশিং মিশ্রণীয় হয় যখন আপনার টিম একটি পৃষ্ঠায় নিয়মগুলো ইঙ্গিত করতে পারে ও একমত হয় কী ঘটবে। লক্ষ্য পারফেকশন নয়—পরিবর্তনের মধ্যেও ভব্যমানযোগ্য আচরণ।
প্রতি স্ক্রিনের জন্য একটি ছোট টেবিল লিখুন যাতে দ্রুত রিভিউ করা যায়: স্ক্রিন নাম ও প্রধান ডেটা, ক্যাশ লোকেশন ও কী, freshness নিয়ম (TTL, ইভেন্ট-ভিত্তিক, বা ম্যানুয়াল), ইনভ্যালিডেশন ট্রিগার, এবং রিফ্রেশ চলাকালীন ইউজারকে কি দেখাবেন।
টিউন করার সময় হালকা লগিং যোগ করুন: ক্যাশ হিট, মিস, এবং কেন রিফ্রেশ ঘটেছে (TTL এক্সপায়ার, ইউজার পুল, অ্যাপ রিসিউম, মিউটেশন সম্পন্ন)। কেউ যখন বলবে “এই লিস্ট বিকৃত লাগছে”, সেই লগগুলো বাগ নির্ণয়ে সহায়ক হবে।
সরল TTL দিয়ে শুরু করুন, পরে ব্যবহারকারী কী লক্ষ্য করে তা দেখে সূক্ষ্ম সমন্বয় করুন। নিউজ ফিড ৫–১০ মিনিট স্টেইল মানতে পারে, কিন্তু অর্ডার স্ট্যাটাস স্ক্রিন রিসিউমে এবং যে কোনো চেকআউট অ্যাকশনের পরে রিফ্রেশ চাইবে।
দ্রুত Flutter অ্যাপ বানাচ্ছেন—তাহলে ডেটা লেয়ার ও ক্যাশ নিয়ম আগে থেকেই আউটলাইন করে নেওয়া সাহায্য করতে পারে। টিম Koder.ai (koder.ai) ব্যবহার করলে Planning Mode-এ প্রতিটি স্ক্রিনের নিয়ম প্রথমে লিখে তারপর তা মেলে করে নির্মাণ করা সুবিধাজনক।
রিফ্রেশ আচরণ টিউন করার সময় স্থিতিশীল স্ক্রিনগুলো রক্ষা করুন যাতে নতুন নিয়ম হঠাৎ করে ফ্লিকার, খালি স্টেট, বা নেভিগেশনে অসামঞ্জস্য না নিয়ে আসে—স্ন্যাপশট ও রোলব্যাক সাহায্য করতে পারে।
একটি নির্ভরযোগ্য নিয়ম দিয়ে শুরু করুন: একটি স্ক্রিন ঠিক কী দেখাতে পারে তা স্পষ্ট করে দিন (ক্যাশ করা), কখন অবশ্যই রিফ্রেশ করতে হবে, এবং রিফ্রেশ চলাকালীন ব্যবহারকারী কী দেখতে পাবে। যদি আপনি এক বাক্যে নিয়মটি স্পষ্ট করে বলতে না পারেন, অ্যাপটি শেষ পর্যন্ত অনিয়মিত বোধ করাবে।
ক্যাশকৃত ডেটাকে একটি freshness স্টেটে ধরুন। যদি এটা তাজা হয়, দেখান। যদি এটা পুরনো কিন্তু ব্যবহারযোগ্য হয়, এখনই দেখান এবং পেছনে রিফ্রেশ চালান। যদি এটা রিফ্রেশ আবশ্যক হয়, দেখার আগে ফেচ করুন (অথবা লোডিং/অফলাইন স্টেট দেখান)। এভাবে UI প্রতিবার একই রকম আচরণ করবে, যে ফলে “কখন আপডেট হয়েছে”–এর অনিশ্চয়তা কমে।
প্রতিবার খুব বেশি পড়া হয় এবং একটু পুরনো হলে ক্ষতি হয় না এমন ডেটা ক্যাশ করুন: ফিড, ক্যাটালগ, রেফারেন্স ডেটা, এবং বেসিক প্রোফাইল ইনফো। অর্থ বা সময়-নির্ভর ডেটা (ব্যালেন্স, স্টক, ETAs, অর্ডার স্ট্যাটাস) সতর্কতার সাথে ক্যাশ করুন: গতি জন্য ক্যাশ করা যায়, কিন্তু সিদ্ধান্তের ঠিক আগে অবশ্যই সার্ভার নিশ্চিত করুন।
মেমরি সেশন-ভিত্তিক দ্রুত রিপ্লে জন্য, ডিস্ক কী-ভ্যালু ছোট আইটেমের জন্য, এবং লোকাল ডাটাবেস বড়, স্ট্রাকচার্ড বা অফলাইন আচরণের জন্য। সাধারণত: মেমরি = সেশন-রিইউজ, ডিস্ক কী-ভ্যালু = ছোট ও সল্প-পরিবর্তনশীল, ডাটাবেস = বড়/কোয়েরি-নির্ভর।
প্লেইন TTL অনেক স্ক্রিনের জন্য ভালো ডিফল্ট: একটি সময় ধরে ডেটাকে তাজা গণ্য করুন, তারপর স্টেইল হিসেবে চিহ্নিত করুন। আরও ভালো UX পেতে পারেন stale-while-revalidate প্যাটার্নে: এখন দেখান, পেছনে রিফ্রেশ করুন, এবং ফলাফল আলাদা হলে UI আপডেট করুন—এতে ফ্ল্যাশ বা ব্ল্যাঙ্ক স্ক্রিন কম হয়।
ক্যাশ তখনই ইনভ্যালিড করুন যখন কোন ইভেন্ট ক্যাশকে রিফ্রেশ করার তুলনায় অবিশ্বাস্য করে তোলে: ইউজার এডিট/ক্রিয়েশন, লগইন/লগআউট বা অ্যাকাউন্ট সুইচ, অ্যাপ রিসিউম (যদি ডেটা TTL-এ পুরনো হয়ে যায়), এবং স্পষ্ট ইউজার রিফ্রেশ। ট্রিগারগুলো ছোট ও এক্সপ্লিসিট রাখুন যাতে বারবার বা একদম না-হওয়া রিফ্রেশ ঠেকানো যায়।
দুই স্ক্রিন যদি একই ডেটার স্বাধীন কপি রেন্ডার করে, অনিশ্চয়তা জন্মে। একটিই source of truth রাখুন: লিস্ট ও ডিটেইল উভয়ই একই শেয়ার্ড স্টোর দেখুক। ডিটেইলে সম্পাদনার পরে লোকালি স্টোর আপডেট করুন যাতে ব্যাক করলে লিস্টে নতুন মানই দেখায়।
পে-লোডের পাশে সর্বদা মেটাডেটা রাখুন: timestamp এবং ইউজার আইডি বিশেষ করে গুরুত্বপূর্ণ। লগআউট বা অ্যাকাউন্ট সুইচ হলে ইউজার-স্কোপড ক্যাশগুলো তৎক্ষণাৎ মুছে দিন বা আলাদা করুন এবং পুরাতন ইউজারের ইন-ফ্লাইট রিকোয়েস্ট ক্যানসেল করুন—না হলে সাময়িকভাবে আগের ইউজারের ডেটা দেখা যেতে পারে।
ডিফল্ট আচরণ হিসেবে পুরনো ডেটা দেখতেই রাখুন এবং ছোট, সুস্পষ্ট একটি ত্রুটির নোট দেখান+রিট্রাই অপশন দিন। যদি কোনো স্ক্রিন পুরনো ডেটা দেখালে নিরাপদ না হয়, তবে সেটাকে must-refresh করে লোডার বা অফলাইন মেসেজ দেখান—পুরনো মানকে বিশ্বাস করা বন্ধ করুন।
ক্যাশ লজিক ডেটা লেয়ারে (যেমন repository) রাখুন যাতে প্রতিটি স্ক্রিন একই আচরণ অনুসরণ করে। স্ক্রিনগুলোকে বাস্তুতান্ত্রিকভাবে প্ল্যানিং মোডে per-screen freshness ও invalidation রুল লিখে দিন (Koder.ai-তে সহজ হলে Planning Mode ব্যবহার করে), তারপর UI কেবল স্টেটগুলোর উপর রিএক্ট করুক, নিজে রিফ্রেশ লজিক তৈরি না করে।