Dependency Injection و Service Layer
Dependency Injection و Service Layer
محتوای درس 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، امنیت و نیازهای نگهداری پروژه هماهنگ بماند.