فصل 9 از 24 درس 1 از 1

Dependency Injection و Service Layer

Dependency Injection و Service Layer

بخشی از آموزش جامع ASP.NET Core Web API با .NET 10

محتوای درس Dependency Injection و Service Layer

.NET 10 | Service Lifetimes و جداسازی مسئولیت‌ها

Dependency Injection وابستگی‌ها را از ساخت مستقیم Objectها جدا می‌کند و Composition برنامه را متمرکز نگه می‌دارد. Service Layer نیز منطق Application را از Controller و جزئیات HTTP تفکیک می‌کند.

این فصل با تمرکز بر قراردادهای قابل اتکا، مرزبندی مسئولیت‌ها و رفتار قابل پیش‌بینی در محیط واقعی تنظیم شده است. مثال‌ها بر پایه ASP.NET Core و .NET 10 نوشته شده‌اند.

1. چارچوب موضوع و مفاهیم اصلی

Dependency Injection وابستگی‌ها را از ساخت مستقیم Objectها جدا می‌کند و Composition برنامه را متمرکز نگه می‌دارد. Service Layer نیز منطق Application را از Controller و جزئیات HTTP تفکیک می‌کند.

واژگان و قراردادهای این بخش باید در سراسر پروژه یکدست بمانند؛ تفاوت میان لایه HTTP، منطق برنامه و زیرساخت زمانی روشن می‌ماند که مسئولیت هر جزء به‌صورت صریح تعریف شود.

مفهومکارکرد
DI Containerسازنده Object graph
Transientنمونه جدید در هر Resolve
Scopedنمونه مشترک در Scope
Singletonنمونه واحد برنامه
Interfaceقرارداد وابستگی
Service Layerمنطق Application

2. ساختار پیشنهادی در پروژه

Controller تنها Service موردنیاز را دریافت می‌کند. Service به DbContext یا زیرساخت‌های لازم وابسته است و Registrationها در Extension Methodهای مشخص گروه‌بندی می‌شوند.

builder.Services.AddScoped<IProductService, ProductService>();
builder.Services.AddSingleton<IClock, SystemClock>();

public sealed class ProductsController(IProductService service) : ControllerBase
{
    [HttpGet("{id:int}")]
    public async Task<IActionResult> GetById(int id, CancellationToken ct)
        => Ok(await service.GetByIdAsync(id, ct));
}

3. سناریوی اجرایی

ProductService Query و Ruleهای Application را مدیریت می‌کند. Controller از EF Core آگاه نیست و در تست می‌توان IProductService را Mock یا Fake کرد.

در این سناریو، قرارداد ورودی و خروجی، مسیر شکست و رفتار قابل مشاهده سرویس باید پیش از جزئیات پیاده‌سازی مشخص شود. این رویکرد باعث می‌شود تغییرات بعدی بدون وابستگی پنهان و با امکان تست دقیق انجام شوند.

public sealed class ProductService(AppDbContext db) : IProductService
{
    public Task<ProductResponse?> GetByIdAsync(int id, CancellationToken ct)
        => db.Products.AsNoTracking()
            .Where(p => p.Id == id)
            .Select(p => new ProductResponse(p.Id, p.Name, p.Price))
            .FirstOrDefaultAsync(ct);
}

4. اصول طراحی و نگهداری

پیاده‌سازی قابل نگهداری تنها به درست کار کردن در مسیر موفق محدود نیست. مرزهای مسئولیت، قابلیت مشاهده، امنیت، رفتار در خطا و امکان توسعه تدریجی باید هم‌زمان بررسی شوند.

  • وابستگی Singleton به Service Scoped بدون Scope جدید ایجاد نشود.
  • Constructorها فقط Dependency واقعی دریافت کنند.
  • Serviceهای بسیار بزرگ بر اساس Use Case شکسته شوند.
  • Interface تنها در جایی اضافه شود که Contract مستقل ارزش دارد.

5. خطاهای رایج و کنترل آن‌ها

بخش مهمی از کیفیت یک API در نحوه جلوگیری از خطاهای تکرارشونده مشخص می‌شود. موارد زیر باید در بازبینی کد و تست‌های قبل از انتشار کنترل شوند.

  • Service Locator و Resolve دستی در منطق روزمره.
  • Circular Dependency میان Serviceها.
  • ثبت DbContext به‌عنوان Singleton.
  • انتقال HttpContext به تمام لایه‌های داخلی.

6. چک‌لیست نهایی فصل

  • Lifetime هر Service با رفتار آن هماهنگ است.
  • Controller منطق داده حجیم ندارد.
  • Dependency Chain قابل Resolve است.
  • Registrationها ساختارمند و قابل بازبینی هستند.

7. جمع‌بندی

مفاهیم این فصل زمانی کامل محسوب می‌شوند که هم مسیر موفق و هم مسیر خطا قابل پیش‌بینی، قابل تست و قابل مشاهده باشند. طراحی نهایی باید با Contract API، امنیت و نیازهای نگهداری پروژه هماهنگ بماند.