হ্যালো! আমি তোমার Simple Coding Tutor। বর্তমান লেকচারটি তোমার কোর্সের Section 15-এর “Add Country - xUnit Test - Part 2”। এই লেকচারে TDD (Test-Driven Development) এর কনসেপ্টকে আরও এক ধাপ এগিয়ে নিয়ে যাওয়া হয়েছে এবং আর্কিটেকচারাল সেপারেশন (Architectural Separation) এর উপর জোর দেওয়া হয়েছে।

চলো, এই লেকচারটির সম্পূর্ণ বিষয়বস্তু ধাপে ধাপে এবং সহজভাবে বুঝে নিই।

📝 লেকচার সামারি (Summary for Quick Revision)

ভবিষ্যতে দ্রুত রিভিশন দেওয়ার জন্য পুরো লেকচারের মূল বিষয়গুলো নিচে তালিকাভুক্ত করা হলো:

  • Response DTO: CountryResponse নামে একটি নতুন DTO ক্লাস তৈরি করা হয়েছে, যা ডেটাস্টোর থেকে CountryId এবং CountryName রিটার্ন করবে।
  • Extension Method: Country (Domain Model) অবজেক্টকে CountryResponse (DTO) অবজেক্টে সহজে কনভার্ট করার জন্য ToCountryResponse() নামে একটি Extension Method তৈরি করা হয়েছে।
  • Interface Creation: ICountryService নামে একটি ইন্টারফেস তৈরি করে সেখানে AddCountry মেথডের সিগনেচার ডিফাইন করা হয়েছে।
  • Test Class Setup: TDD রুল অনুযায়ী টেস্টিংয়ের জন্য CountriesServiceTest ক্লাস তৈরি করা হয়েছে এবং প্রয়োজনীয় Project References অ্যাড করা হয়েছে।
  • Services Project: আসল বিজনেস লজিক রাখার জন্য Services নামে একটি সম্পূর্ণ নতুন Class Library প্রজেক্ট তৈরি করা হয়েছে।
  • Dummy Implementation: TDD ফলো করার কারণে, আপাতত CountriesService ক্লাসে AddCountry মেথডের ভেতরে শুধু throw new NotImplementedException() রাখা হয়েছে (পরে এটি ইমপ্লিমেন্ট করা হবে)।

🧠 Comprehensive Breakdown (বিস্তারিত আলোচনা)

১. CountryResponse DTO তৈরি করা [Priority: 10/10]

আগের লেকচারে আমরা CountryAddRequest তৈরি করেছিলাম, যা শুধু ইউজারের ইনপুট (CountryName) রিসিভ করত। কিন্তু Service যখন কাজ শেষে রেসপন্স পাঠাবে, তখন শুধু নাম পাঠালে হবে না, সিস্টেম জেনারেটেড আইডিও (CountryId) পাঠাতে হবে। এর জন্যই CountryResponse DTO প্রয়োজন।

  • কেন এটি গুরুত্বপূর্ণ? Controller বা ক্লায়েন্ট সাইডে আমরা কখনো Domain Model (Country) সরাসরি পাঠাবো না। এর ফলে ডেটাবেসের আসল স্ট্রাকচার হাইড (hide) থাকে এবং সিকিউরিটি বাড়ে।

Code Implementation (ServiceContracts/DTO/CountryResponse.cs):

using System;
 
namespace ServiceContracts.DTO
{
    /// <summary>
    /// DTO class that is used as return type for most of CountryService methods
    /// </summary>
    public class CountryResponse
    {
        public Guid CountryId { get; set; }
        public string? CountryName { get; set; }
    }
}
 

২. Extension Method ব্যবহার করে Object Conversion [Priority: 9/10]

Service-এর ভেতরে আমাদের কাছে Country (Domain) অবজেক্ট থাকবে, কিন্তু মেথড থেকে রিটার্ন করতে হবে CountryResponse (DTO)। এই রূপান্তরের কাজ করার জন্য একটি Extension Method তৈরি করা হয়েছে।

  • কেন Extension Method? আমরা চাইলে মূল Country ক্লাসের ভেতরেই এই কনভার্শন লজিক লিখতে পারতাম। কিন্তু ডোমেইন মডেলকে সবসময় ক্লিনার রাখা উচিত। Extension Method ব্যবহার করলে মূল ক্লাসের কোনো কোড পরিবর্তন না করেই নতুন মেথড অ্যাড করা যায়।

Code Implementation (ServiceContracts/DTO/CountryExtensions.cs):

using Entities;
 
namespace ServiceContracts.DTO
{
    public static class CountryExtensions
    {
        /// <summary>
        /// Converts Country Domain Model to CountryResponse DTO
        /// </summary>
        public static CountryResponse ToCountryResponse(this Country country) // 'this' keyword makes it an Extension Method
        {
            return new CountryResponse()
            {
                CountryId = country.CountryID,
                CountryName = country.CountryName
            };
        }
    }
}
 

৩. ICountryService Interface তৈরি করা [Priority: 10/10]

ServiceContracts প্রজেক্টে একটি ইন্টারফেস তৈরি করা হয়েছে, যা আমাদের Controller এবং Service এর মাঝে একটি চুক্তি বা Contract হিসেবে কাজ করবে।

  • কেন এটি গুরুত্বপূর্ণ? Dependency Injection (DI) ইমপ্লিমেন্ট করার জন্য এবং Unit Test এ Service-কে Mock করার জন্য Interface থাকা বাধ্যতামূলক।

Code Implementation (ServiceContracts/ICountryService.cs):

using ServiceContracts.DTO;
 
namespace ServiceContracts
{
    /// <summary>
    /// Represents business logic for manipulating Country entity
    /// </summary>
    public interface ICountryService
    {
        /// <summary>
        /// Adds a country object to the list of countries
        /// </summary>
        /// <param name="countryAddRequest">Country object to add</param>
        /// <returns>Returns the country object after adding it (including newly generated country id)</returns>
        CountryResponse AddCountry(CountryAddRequest? countryAddRequest);
    }
}
 

৪. Test Class Setup এবং TDD Principle [Priority: 8/10]

TDD (Test-Driven Development) এর সবচেয়ে গুরুত্বপূর্ণ রুল হলো: “The implementation should follow the unit test. But the unit test should not follow the implementation.” অর্থাৎ, আগে টেস্ট লিখতে হবে (যাতে রিকোয়ারমেন্ট ক্লিয়ার হয়), এরপর আসল লজিক ইমপ্লিমেন্ট করতে হবে।

  • CRUDTests প্রজেক্টে CountriesServiceTest ক্লাস তৈরি করা হয়েছে।
  • টেস্ট প্রজেক্ট যেন Entities এবং ServiceContracts প্রজেক্টের ক্লাসগুলো চিনতে পারে, সেজন্য Add Project Reference ব্যবহার করে এই দুটি প্রজেক্ট যুক্ত করা হয়েছে।

৫. Services Project এবং Dummy Implementation [Priority: 9/10]

আর্কিটেকচারকে মডুলার (Modular) রাখার জন্য আসল বিজনেস লজিক লেখার জন্য Services নামে একটি সম্পূর্ণ নতুন Class Library প্রজেক্ট তৈরি করা হয়েছে।

  • Services প্রজেক্টে ServiceContracts এর রেফারেন্স অ্যাড করা হয়েছে।
  • CountriesService ক্লাসে ICountryService ইন্টারফেস ইমপ্লিমেন্ট করা হয়েছে।
  • যেহেতু আমরা TDD ফলো করছি এবং এখনই আসল কোড লিখব না, তাই মেথডের ভেতরে একটি Dummy Exception থ্রো করা হয়েছে।

Code Implementation (Services/CountriesService.cs):

using ServiceContracts;
using ServiceContracts.DTO;
using System;
 
namespace Services
{
    public class CountriesService : ICountryService
    {
        public CountryResponse AddCountry(CountryAddRequest? countryAddRequest)
        {
            // ডামি ইমপ্লিমেন্টেশন। এটি ইউনিট টেস্ট রান করার সময় Exception থ্রো করবে
            throw new NotImplementedException(); 
        }
    }
}
 

💡 Best Practices & Modern .NET Updates

১. Object Mapping Best Practice (.NET 10 Context): লেকচারে Extension Method ব্যবহার করে Object Mapping (Domain to DTO) দেখানো হয়েছে যা একটি ভালো পদ্ধতি। তবে মডার্ন .NET (যেমন .NET 8, 9 বা 10) এ রিয়েল-ওয়ার্ল্ড এন্টারপ্রাইজ লেভেল প্রজেক্টে ডেটা ম্যাপিংয়ের জন্য AutoMapper বা আরও দ্রুতগামী Mapster লাইব্রেরি ব্যবহার করা হয়। এতে বয়লারপ্লেট কোড (boilerplate code) অনেক কমে যায়।

  • Example (Mapster): var response = country.Adapt<CountryResponse>();

২. Global Usings (.NET 10 Features): তুমি লক্ষ্য করবে যে প্রতিটা ফাইলে using System;, using Entities; লিখতে হচ্ছে। .NET 10 (C# 10 থেকে শুরু) এ Global Usings ফিচার আছে। তুমি চাইলে টেস্ট বা সার্ভিস প্রজেক্টের রুটে একটি GlobalUsings.cs ফাইল তৈরি করে সেখানে global using Entities; লিখে দিতে পারো। তাহলে পুরো প্রজেক্টের আর কোনো ফাইলে বারবার using ডিক্লেয়ার করতে হবে না।

৩. TDD Rule (Red-Green-Refactor): সবসময় TDD এর তিনটি ফেজ মাথায় রাখবে:

  • Red: প্রথমে টেস্ট লিখো এবং রান করো (টেস্ট ফেইল করবে বা NotImplementedException খাবে)।
  • Green: শুধু ততটুকুই কোড লেখো যতটুকু টেস্ট পাস করার জন্য দরকার।
  • Refactor: কোডটাকে ক্লিন করো, অপ্টিমাইজ করো।

⌨️ Editor Shortcuts: ইন্সট্রাক্টর লেকচারে Interface ইমপ্লিমেন্ট করার জন্য Quick Actions এর কথা বলেছেন।

  • Visual Studio Shortcut: ICountryService এর উপর মাউস রেখে Ctrl + . (Control + Dot) চাপলে “Implement Interface” অপশন চলে আসবে।
  • Visual Studio Code (VS Code) Shortcut: VS Code-এও ঠিক একইভাবে ইন্টারফেসের নামের উপর কার্সর রেখে Ctrl + . (Windows) বা Cmd + . (Mac) চাপলে মেনু আসবে, যেখান থেকে তুমি খুব সহজেই ইন্টারফেস ইমপ্লিমেন্ট করতে পারবে।