আমি আপনার এক্সপার্ট সফটওয়্যার ইঞ্জিনিয়ারিং ট্রেইনার। কোর্স আউটলাইন অনুযায়ী, আমরা এখন Section 15: xUnit testing-এর ভেতরে আছি। আগের লেকচারে আমরা GetAllPersons মেথডের জন্য Unit Test লিখেছিলাম, যা ফেইল করেছিল। এই লেকচারে (যা মাত্র ২ মিনিটের একটি ছোট লেকচার) আমরা মেথডটির আসল লজিক (Implementation) লিখব, যাতে সবগুলো টেস্ট পাস করে।
চলুন, আজকের লেকচারটি বিস্তারিতভাবে বুঝে নেওয়া যাক।
📝 Lecture Summary (Quick Revision)
ভবিষ্যতে দ্রুত রিভিশন দেওয়ার জন্য সম্পূর্ণ লেকচারের মূল কাজগুলো নিচে লিস্ট আকারে দেওয়া হলো:
- Remove Dummy Code:
PersonsServiceক্লাসে থাকাthrow new NotImplementedException();মুছে ফেলা। - Data Transformation (LINQ): ডাটাবেস বা ডাটা স্টোর (
_personsলিস্ট) থেকে সবগুলোPerson(Domain Model) কে তুলে আনা এবং LINQ এরSelectমেথড ব্যবহার করে সেগুলোকেPersonResponse(DTO) তে রূপান্তর করা। - Lambda Expression:
Selectমেথডের ভেতরে ল্যাম্বডা এক্সপ্রেশন (temp => temp.ToPersonResponse()) ব্যবহার করে প্রতিটি আইটেমকে ম্যাপ করা। - Conversion to List:
Selectথেকে রিটার্ন হওয়াIEnumerableকালেকশনকে.ToList()কল করেList-এ কনভার্ট করা এবং রিটার্ন করা। - Test Verification: টেস্ট রান করে দেখা যে
GetAllPersonsএবং আগের লেকচারে ফেইল হওয়াAddPerson_ProperPersonDetailsটেস্টগুলো এখন সফলভাবে পাস করেছে।
🧠 Comprehensive Breakdown
এই লেকচারে আমরা অত্যন্ত ছোট কিন্তু গুরুত্বপূর্ণ একটি ডেটা ট্রান্সফরমেশন লজিক ইমপ্লিমেন্ট করেছি। চলুন প্রতিটি ধাপ বিস্তারিতভাবে বুঝে নিই।
১. Dummy Implementation রিমুভ করা (Priority: 5/10)
Concept & Why: Test Driven Development (TDD) এর নিয়ম অনুযায়ী আমরা প্রথমে ইন্টারফেস ইমপ্লিমেন্ট করার সময় একটি ডামি এক্সেপশন (NotImplementedException) রেখে দিয়েছিলাম। এখন যেহেতু আমরা আসল লজিক লিখছি, তাই এটি মুছে ফেলতে হবে।
২. LINQ Select মেথড ব্যবহার করে Data Transformation (Priority: 10/10)
Concept & Why: আমাদের _persons লিস্টে যে ডেটাগুলো আছে, সেগুলো হলো Person টাইপের (Domain Model)। কিন্তু মেথডটির রিটার্ন টাইপ হলো List<PersonResponse> (DTO)। আমরা কখনোই সরাসরি Domain Model ক্লায়েন্ট বা Controller-কে দেব না।
এজন্য আমরা LINQ এর Select মেথড ব্যবহার করেছি। Select মেথডটি মূলত ডাটা ট্রান্সফর্ম বা ম্যাপ করার জন্য ব্যবহৃত হয়।
- এটি
_personsলিস্টের প্রতিটি ইলিমেন্টকে (Element) একে একে লুপের মতো ধরে। - ল্যাম্বডা এক্সপ্রেশনের মাধ্যমে (যেমন
temp => temp.ToPersonResponse()) প্রতিটিPersonঅবজেক্টকে আমাদের আগের তৈরি করা হেল্পার মেথড দিয়েPersonResponse-এ কনভার্ট করে।
// প্রতিটি Person কে PersonResponse এ ম্যাপ করা হচ্ছে
_persons.Select(temp => temp.ToPersonResponse());
৩. IEnumerable থেকে List-এ কনভার্সন (Priority: 8/10)
Concept & Why: LINQ এর Select মেথডটি ডিফল্টভাবে একটি IEnumerable<T> রিটার্ন করে, যা একটি জেনেরিক কালেকশন। কিন্তু আমাদের ইন্টারফেসে মেথড সিগনেচার বলা আছে এটি List<PersonResponse> রিটার্ন করবে। তাই Select মেথডের শেষে .ToList() এক্সটেনশন মেথড কল করতে হয়।
// সম্পূর্ণ ইমপ্লিমেন্টেশন
public List<PersonResponse> GetAllPersons()
{
return _persons.Select(temp => temp.ToPersonResponse()).ToList();
}
৪. Unit Test Verification (Priority: 8/10)
Concept & Why: ইমপ্লিমেন্টেশন শেষ করার পর Test Explorer থেকে “Run All Tests” করা হয়।
GetAllPersons_EmptyListএবংGetAllPersons_AddFewPersonsটেস্ট দুটি পাস করে, কারণ আমাদের লজিক সঠিক।- সবচেয়ে ইন্টারেস্টিং বিষয় হলো, আগের লেকচারগুলোতে
AddPerson_ProperPersonDetailsনামের যে টেস্টটি ফেইল করছিল (কারণ তার ভেতরেGetAllPersonsকল করা ছিল), সেটিও এখন পাস করেছে। এর মাধ্যমে TDD-এর পুরো সাইকেলটি সফলভাবে সম্পন্ন হলো।
🚀 Modern C# (.NET 10) Updates & Smarter Approach
লেকচারে দেখানো কোডটি অত্যন্ত ক্লিন এবং .NET 10 এও এটি এভাবেই লেখা হয়। তবে রিয়েল-ওয়ার্ল্ড এন্টারপ্রাইজ লেভেল প্রোজেক্টে কিছু মডার্ন আর্কিটেকচারাল পরিবর্তন আনা হয়:
1. Using AutoMapper / Mapster instead of Manual Extension Methods:
লেকচারে আমরা ToPersonResponse() নামের একটি কাস্টম এক্সটেনশন মেথড লিখেছিলাম ম্যাপ করার জন্য। প্রজেক্ট বড় হলে এরকম শত শত মেথড লেখা এবং মেইনটেইন করা কঠিন। আধুনিক .NET প্রোজেক্টে AutoMapper বা Mapster এর মতো লাইব্রেরি ব্যবহার করা হয়।
Modern Code with AutoMapper:
// AutoMapper ব্যবহার করলে কোড আরও ক্লিন হয়ে যায়
public List<PersonResponse> GetAllPersons()
{
return _mapper.Map<List<PersonResponse>>(_persons);
}
2. Return Type IReadOnlyList or IEnumerable:
মডার্ন C# এবং ক্লিন আর্কিটেকচার গাইডলাইন অনুযায়ী, মেথড থেকে যদি এমন কোনো ডেটা রিটার্ন করা হয় যা ক্লায়েন্ট শুধু রিড (Read) করবে (কোনো কিছু Add/Remove করবে না), সেখানে List<T> এর বদলে IEnumerable<T> বা IReadOnlyList<T> রিটার্ন করা বেস্ট প্র্যাকটিস। এতে মেমরি অ্যালোকেশন কিছুটা কম হয় এবং পারফরম্যান্স বাড়ে।
Modern Interface Signature Example:
// IPersonsService.cs
IEnumerable<PersonResponse> GetAllPersons();
// PersonsService.cs
public IEnumerable<PersonResponse> GetAllPersons()
{
// .ToList() কল করার আর প্রয়োজন নেই, যা মেমরি বাঁচায়
return _persons.Select(p => p.ToPersonResponse());
}
🏆 Best Practices (For Data Fetching and Mapping)
- Avoid
foreachfor Mapping: ডেটা ট্রান্সফর্ম করার জন্য কখনোই ম্যানুয়ালিListডিক্লেয়ার করেforeachলুপ চালাবেন না। সব সময় LINQ এরSelectব্যবহার করুন। এটি কোডকে Declarative এবং রিডেবল করে তোলে। - Never Return Domain Entities: সার্ভিস লেয়ার থেকে ডেটা রিট্রিভ করার সময় (যেমন
GetAll...মেথডগুলোতে) সব সময় খেয়াল রাখবেন যেন কোনোভাবেই ডাটাবেস Entity রিটার্ন না হয়ে যায়। সব সময় DTO (Data Transfer Object) তে ম্যাপ করে তারপর রিটার্ন করবেন। - Use Meaningful Lambda Parameters: ল্যাম্বডা এক্সপ্রেশনের প্যারামিটার হিসেবে
tempবাxব্যবহারের চেয়ে ডোমেইনের সাথে সম্পর্কিত নাম (যেমনperson => person.ToPersonResponse()) ব্যবহার করা ভালো প্র্যাকটিস, এতে কোডের রিডেবিলিটি বাড়ে।