স্বাগতম! আজকে আমরা SOLID প্রিন্সিপালগুলোর মধ্যে ‘D’ অর্থাৎ Dependency Inversion Principle (DIP) নিয়ে একটি দারুণ রিক্যাপ সেশন করব।

আপনার Outline অনুযায়ী, আপনি এখন Section 23: SOLID Principles এর Dependency Inversion Principle (Revision) টপিকে আছেন। এটি মূলত Section 12 এর একটি রিভিশন। আজকে আমরা দেখব কীভাবে এই প্রিন্সিপালটি একটি প্রজেক্টের আর্কিটেকচারকে Flexible এবং Decoupled রাখে। চলুন শুরু করি!


📝 Quick Summary for Revision

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

  • The Core Problem: High-level module যদি Low-level module এর Object সরাসরি তৈরি করে (Tightly coupled), তবে মেইনটেইন করা কঠিন হয়ে যায়।
  • The Solution (DIP): High-level এবং Low-level উভয়কেই Abstraction (যেমন: Interface) এর উপর নির্ভর করতে হবে।
  • Dependency Injection: Client (যেমন: Controller) সরাসরি Dependency (যেমন: Service) কল না করে, Constructor বা Method Injection এর মাধ্যমে Interface কে রিসিভ করবে।
  • Independent Development: Interface (Contract) ঠিক থাকলে Client এবং Dependency উভয়েই একে অপরের জন্য অপেক্ষা না করে স্বাধীনভাবে (Independently) কোড করা যায়।
  • IoC Container: Program.cs ফাইলে AddTransient, AddScoped বা AddSingleton ব্যবহার করে Interface এবং Class এর ম্যাপিং রেজিস্টার করতে হয়।

🧠 Comprehensive Breakdown

লেকচারের প্রতিটি বিষয় নিচে বিস্তারিতভাবে আলোচনা করা হলো এবং বোঝার সুবিধার্থে কোড ইমপ্লিমেন্টেশন যুক্ত করা হলো।

The Problem of Tight Coupling [Priority: 10/10]

Why: যখন একটি Class অন্য একটি Class এর Object সরাসরি new কিওয়ার্ড দিয়ে তৈরি করে, তখন তারা একে অপরের সাথে শক্তভাবে জুড়ে যায় (Tightly coupled)।

  • এর ফলে Low-level module (যেমন: Database Access) এ কোনো পরিবর্তন আসলে High-level module (যেমন: Business Logic/Controller) ব্রেক করতে পারে।
  • এছাড়াও Low-level module এর কোড কমপ্লিট না হওয়া পর্যন্ত High-level module এর ডেভেলপারদের অপেক্ষা করতে হয়।
// ❌ Bad Practice (Tightly Coupled)
public class PersonsController 
{
    // Controller is directly depending on the concrete class
    private PersonsService _personsService = new PersonsService(); 
}
 

Dependency Inversion Principle (DIP) [Priority: 10/10]

Why: এই সমস্যার সমাধানের জন্যই DIP এর উৎপত্তি। এর মূল নিয়ম হলো: High-level modules কখনোই Low-level modules এর উপর নির্ভর করবে না, উভয়েই Abstractions (Interface) এর উপর নির্ভর করবে।

Practical Implementation in ASP.NET Core [Priority: 10/10]

লেকচারে দেখানো Persons অ্যাপের উদাহরণটি চলুন ধাপে ধাপে কোডের মাধ্যমে দেখি।

1. The Abstraction (Interface): প্রথমে আমাদের একটি Contract বা Interface তৈরি করতে হবে। এটি হবে আমাদের Abstraction।

public interface IPersonsService
{
    void AddPerson();
    void GetPerson();
}
 

2. The Dependency (Implementation): Low-level module (Service Class) এই Interface কে ইমপ্লিমেন্ট করবে।

public class PersonsService : IPersonsService
{
    public void AddPerson() { /* Implementation logic */ }
    public void GetPerson() { /* Implementation logic */ }
}
 

3. The Client (High-level Module): PersonsController সরাসরি PersonsService কে কল করবে না, বরং IPersonsService কে Constructor Injection এর মাধ্যমে রিসিভ করবে।

public class PersonsController : ControllerBase
{
    private readonly IPersonsService _personsService;
 
    // ✅ Constructor Injection
    public PersonsController(IPersonsService personsService)
    {
        _personsService = personsService;
    }
 
    // Now you can use _personsService without knowing the exact class behind it
}
 

4. IoC Container Configuration: Runtime এ Service Provider যেন বুঝতে পারে IPersonsService চাইলে তাকে PersonsService এর Object দিতে হবে, সেজন্য একে Service Container এ রেজিস্টার করতে হবে।

// Inside Program.cs or ConfigureServices extension
builder.Services.AddTransient<IPersonsService, PersonsService>();
// Note: You can use AddScoped or AddSingleton based on your project's lifetime requirement.
 

🌟 Best Practices

  • Interface First Approach: যেকোনো Business Logic বা Data Access লেয়ার লেখার আগে তার একটি Interface তৈরি করে নিন।
  • Never use ‘new’ for Services: Controller বা High-level class এর ভেতরে ভুলেও new কিওয়ার্ড দিয়ে Service এর Object তৈরি করবেন না। সবসময় Constructor Injection ব্যবহার করুন।
  • Keep Contracts Stable: Interface এর সিগনেচার (Method Name, Parameters) বারবার পরিবর্তন করা থেকে বিরত থাকুন, কারণ এটি Client এবং Implementation উভয়কেই অ্যাফেক্ট করে।

🚀 .NET 10 Context

.NET 10 (এবং C# 12+) এ Primary Constructors ব্যবহার করে Dependency Injection এর কোড আরও অনেক স্মার্ট এবং ক্লিন করা যায়। পুরনো নিয়মে ফিল্ড ডিক্লেয়ার করে অ্যাসাইন করার যে বয়লারপ্লেট (Boilerplate) কোড ছিল, সেটি এখন আর লাগে না।

Updated .NET 10 Smart Implementation:

// ✅ .NET 10 Primary Constructor for Clean DI
public class PersonsController(IPersonsService personsService) : ControllerBase
{
    // No need to declare a private readonly field or a separate constructor body!
    // You can directly use the 'personsService' parameter inside your action methods.
    
    [HttpPost]
    public IActionResult Add()
    {
        personsService.AddPerson();
        return Ok("Person Added Successfully");
    }
}