হ্যালো হাসিব! তুমি এখন Section 12: Dependency Injection-এর প্রথম লেকচার “Services - Part 1” (Lecture 128)-এ আছো। এই সেকশনটি পুরো ASP.NET Core-এর সবচেয়ে গুরুত্বপূর্ণ এবং হার্ট (Heart) বলা যায়।
আজকের লেকচারে আমরা শিখবো Service (বা Business Layer) কী, কেন Controller-এর ভেতরে বিজনেস লজিক লেখা উচিত নয় এবং কীভাবে একটি আলাদা Class Library প্রজেক্ট তৈরি করে Service বানাতে হয়।
চলো, এই লেকচারটি বিস্তারিতভাবে ডিকোড করি।
সারসংক্ষেপ (Quick Revision List)
- Business Logic কী: ইনস্যুরেন্সের প্রিমিয়াম ক্যালকুলেট করা, বয়স চেক করা, বা ডাটাবেসে ডাটা সেভ করার আগে যে ভ্যালিডেশন বা ক্যালকুলেশনগুলো করা হয়, তাকেই Business Logic বলে।
- কেন Controller-এ লজিক লিখবো না: Controller-এর কাজ শুধু ক্লায়েন্টের রিকোয়েস্ট রিসিভ করা এবং রেসপন্স পাঠানো। Controller-এ লজিক লিখলে কোড বিশাল হয়ে যায়, টেস্ট করা কঠিন হয় এবং পুনরায় ব্যবহার (Reuse) করা যায় না।
- Service Layer কী: এটি Presentation Layer (Controller/View) এবং Data Layer (Database)-এর মাঝখানের একটি লেয়ার, যেখানে সমস্ত বিজনেস লজিক লেখা হয়।
- Best Practice: Service ক্লাসগুলোকে ওয়েব প্রজেক্টের ভেতরে না রেখে, একটি আলাদা Class Library প্রজেক্টে রাখা উচিত। এতে কোড অন্যান্য প্রজেক্টেও (যেমন: Mobile App API) রিইউজ করা যায়।
- Naming Convention: প্রাইভেট ফিল্ডের নামের আগে আন্ডারস্কোর (
_) দিতে হয় (যেমন:_cities)। Service ক্লাসের নামের শেষেServiceশব্দটি যুক্ত করতে হয় (যেমন:CitiesService)।
Comprehensive Breakdown
১. Business Logic কী? [Priority: 10/10]
লেকচারার খুব সুন্দর একটি উদাহরণ দিয়েছেন। ধরো তুমি একটি ইনস্যুরেন্স (Insurance) অ্যাপ্লিকেশন বানাচ্ছো। ইউজারের বয়স অনুযায়ী তার ইনস্যুরেন্স প্রিমিয়াম কত হবে, সেই হিসাব করাটা কোনো UI (HTML)-এর কাজ নয়, আবার ডাটাবেসের কাজও নয়। এই ক্যালকুলেশনের কাজটাই হলো Business Logic।
- Validation (বয়স ১৮ এর নিচে কি না)
- Calculation (প্রিমিয়াম অ্যামাউন্ট কত)
- Data Layer-কে কল করা (হিসাব শেষে ডাটাবেসে সেভ করতে বলা) এই সব কাজই Business Logic-এর অংশ।
২. Controller vs Service Layer [Priority: 10/10]
নতুন ডেভেলপাররা প্রায়ই একটি ভুল করে— তারা Controller-এর অ্যাকশন মেথডের ভেতরেই সব if-else, ডাটাবেস কোয়েরি বা লজিক লিখে ফেলে। এটি একটি Bad Practice।
Controller-এ লজিক কেন লিখবো না?
- Fat Controller: Controller-এর কোড অনেক বড় হয়ে যায়, যা পড়া এবং ডিবাগ করা কঠিন।
- Hard to Test: Controller-কে Unit Test করা খুব ঝামেলার।
- Low Reusability: একই লজিক যদি অন্য কোনো Controller-এ লাগে, তখন কোড কপি-পেস্ট করতে হয়।
The Right Way: Presentation Layer (Controller/View) শুধু রিকোয়েস্ট রিসিভ করবে এবং Service-কে ডাকবে। Service তার লজিক শেষ করে ডাটাবেস (Data Access Layer)-কে ডাকবে।
৩. Project Setup এবং Class Library তৈরি [Priority: 10/10]
লেকচারার একটি নতুন ASP.NET Core Empty প্রজেক্ট তৈরি করেছেন, যার নাম দিয়েছেন DIExample। এরপর তিনি Service-এর জন্য একটি আলাদা প্রজেক্ট বানিয়েছেন।
Step A: Class Library তৈরি করা (The Best Practice)
Service ক্লাসগুলোকে DIExample প্রজেক্টের ভেতরেও রাখা যেত, কিন্তু তিনি তা করেননি। তিনি সলিউশনের আন্ডারে একটি নতুন Class Library তৈরি করেছেন।
- Why? কারণ Service-কে আলাদা প্রজেক্টে রাখলে তুমি চাইলে পরবর্তীতে ওই একই Service অন্য কোনো প্রজেক্টে (যেমন: Desktop App বা Mobile API) রেফারেন্স হিসেবে ব্যবহার করতে পারবে।
- How:
- Solution-এর উপর Right Click -> Add -> New Project
- সার্চ করো:
Class Library(অবশ্যই.NET Frameworkনয়, শুধুClass Libraryযা C# এবং .NET/ .NET Standard সাপোর্ট করে)। - প্রজেক্টের নাম দাও
Servicesএবং .NET ভার্সন সেম রাখো।
৪. Service Class তৈরি করা (Implementation) [Priority: 10/10]
এবার Services প্রজেক্টের ভেতরে আমরা আমাদের প্রথম Service Class তৈরি করবো, যা কিছু শহরের নাম রিটার্ন করবে (আপাতত ডাটাবেসের বদলে হার্ডকোডেড লিস্ট ব্যবহার করা হয়েছে)।
Naming Convention Rules:
- Class Name: ক্লাসের নামের শেষে
Serviceলিখতে হবে। যেমনCityএর প্লুরালCities+Service=CitiesService.cs। - Private Fields: প্রাইভেট ভ্যারিয়েবলের নামের শুরুতে
_(underscore) দিতে হবে। যেমন_cities। এটি গ্লোবাল স্ট্যান্ডার্ড।
Code Implementation (Services/CitiesService.cs):
using System.Collections.Generic;
namespace Services
{
public class CitiesService
{
// ১. Private field (Prefix with underscore)
private List<string> _cities;
// ২. Constructor-এর ভেতরে ডাটা ইনিশিয়ালাইজ করা (Mock Data)
public CitiesService()
{
_cities = new List<string>()
{
"London",
"Paris",
"New York",
"Tokyo",
"Rome"
};
}
// ৩. Public Method (Controller এই মেথডকে কল করবে)
public List<string> GetCities()
{
return _cities; // লিস্ট রিটার্ন করে দিবে
}
}
}
- (নোট: রিয়েল-ওয়ার্ল্ড প্রজেক্টে Constructor-এ হার্ডকোডেড ডাটা থাকে না, সেখানে Data Access Layer বা Entity Framework-কে কল করা হয়। যেহেতু আমরা এখনো ডাটাবেস শিখিনি, তাই Mock Data ব্যবহার করা হয়েছে)।
VS / VS Code Shortcuts
- Create Constructor: ক্লাসের ভেতরে
ctorলিখেTab+Tabচাপলে অটোমেটিক কনস্ট্রাক্টর তৈরি হয়ে যায়। - Add Project Reference (VS Code): টার্মিনালে
dotnet add reference ../Services/Services.csprojলিখলে মূল প্রজেক্টেServicesপ্রজেক্টের রেফারেন্স অ্যাড হয়ে যাবে।
Best Practices & .NET 10 Context
Best Practices for Services:
- Strict Layer Separation: Controller কখনোই সরাসরি ডাটাবেসের (DbContext) সাথে কথা বলবে না। Controller কথা বলবে Service-এর সাথে, আর Service কথা বলবে Database-এর সাথে।
- Meaningful Method Names: মেথডের নামগুলো এমন হওয়া উচিত যা পড়লেই কাজ বোঝা যায়। যেমন:
GetCities,CalculatePremium,CreateUser।
.NET 10 Context: ASP.NET Core 10-এ Service Layer বা Class Library বানানোর কনসেপ্ট .NET 6/7/8-এর মতোই হুবহু এক।
তবে আধুনিক C# 12/13 (যা .NET 8, 9 এবং 10 এ ব্যবহৃত হচ্ছে)-তে আমরা Field initialization-কে কনস্ট্রাক্টরের বদলে সরাসরি ফিল্ড লেভেলেই করে ফেলি (Target-typed new expression & Collection Expression):
// Modern .NET 10 style
namespace Services; // File-scoped namespace
public class CitiesService
{
// C# 12 Collection Expression '[]'
private readonly List<string> _cities = ["London", "Paris", "New York", "Tokyo", "Rome"];
public List<string> GetCities()
{
return _cities;
}
}
(এখানে readonly কি-ওয়ার্ড ব্যবহার করা বেস্ট প্র্যাকটিস, কারণ লিস্টটি একবার ইনিশিয়ালাইজ হওয়ার পর আর রি-অ্যাসাইন হওয়ার দরকার নেই)।
হাসিব, Service Layer এবং Class Library-এর এই বেসিক কনসেপ্ট কি ক্লিয়ার হয়েছে? তুমি রেডি থাকলে আমরা “Services - Part 2”-তে মুভ করতে পারি, যেখানে আমরা এই Service-কে Controller-এর সাথে কানেক্ট করবো!