| کانال توسعه‌دهندگان سی‌شارپ |
1.03K subscribers
19 photos
3 videos
21 links
⭕️ کانال توسعه‌دهندگان سی‌شارپ دولوپیکس

💠 دولوپیکس | جامعه توسعه‌دهندگان ایرانی

💎 @Developix
🚀 Developix.ir

📌 پشتیبانی و تبلیغات:
@DevelopixSupport
Download Telegram
در پروژه‌های ASP.NET Core وقتی یک سرویس تعداد زیادی وابستگی دارد، خوانایی، تست‌پذیری و نگهداری کد سخت می‌شود.

ایده‌ی عملی امروز: به‌جای تزریق مستقیم چندین سرویس در یک کلاس، یک Facade یا Aggregate Service بسازید تا زنجیره‌ی وابستگی ساده، قابل تست و خواناتر شود. این رویکرد از Service Locator و constructor bloat جلوگیری می‌کند و مسئولیت‌ها را بهتر جدا می‌کند. 🧩

مثال واقعی: کلاس OrderService که به Inventory، Payment و Notification وابسته است. اول نسخهٔ «بد» با constructor پر از وابستگی‌ها:

<!-- escaped C# code for Telegram -->
public class OrderService
{
private readonly IInventoryService _inventory;
private readonly IPaymentService _payment;
private readonly INotificationService _notification;

public OrderService(IInventoryService inventory, IPaymentService payment, INotificationService notification)
{
_inventory = inventory;
_payment = payment;
_notification = notification;
}

public async Task<bool> PlaceOrderAsync(Order order)
{
if (!await _inventory.ReserveAsync(order)) return false;
var paid = await _payment.ChargeAsync(order);
if (!paid) { await _inventory.ReleaseAsync(order); return false; }
await _notification.SendAsync(order);
return true;
}
}


با الگوی Facade، مجموعهٔ عملیات مرتبط را در یک رابط قرار می‌دهیم تا OrderService ساده شود:

<!-- escaped C# code -->
public interface IOrderProcessor { Task<bool> ProcessAsync(Order order); }

public class OrderProcessor : IOrderProcessor
{
private readonly IInventoryService _inventory;
private readonly IPaymentService _payment;
private readonly INotificationService _notification;

public OrderProcessor(IInventoryService inventory, IPaymentService payment, INotificationService notification) { ... }

public async Task<bool> ProcessAsync(Order order)
{
// همان منطق ترکیبی اینجا قرار می‌گیرد
}
}

public class OrderService
{
private readonly IOrderProcessor _processor;
public OrderService(IOrderProcessor processor) { _processor = processor; }
public Task<bool> PlaceOrderAsync(Order order) => _processor.ProcessAsync(order);
}


نکات عملی:
• با Facade، تست واحد ساده‌تر می‌شود (فقط IOrderProcessor موک می‌شود). 🧪
• مسئولیت‌ها تمیز جدا می‌شوند و هر سرویس کوچک‌تر و تک‌وظیفه‌ای می‌ماند. ✂️
• از تزریق مستقیم تعداد زیادی سرویس در کنترلرها یا سرویس‌های سطح بالا خودداری کنید.

مراجع معتبر: مستندات Microsoft Dependency Injection و الگوهای طراحی در Refactoring.Guru برای جزئیات بیشتر. 📚

شروع به کوچک‌سازی زنجیره‌های تزریق در پروژه کنونی کنید و تغییرات را با تست‌های واحد همراه کنید — تاثیرش را سریع احساس خواهید کرد. 🚀

🔖 #CSharp #سی_شارپ #Facade #DependencyInjection #SOLID #C# #ASP_NET_Core

👤 Developix

💎 Channel: @DevelopixCSharp
2👍1
‏Dependency Injection تمیز در ASP.NET Core برای تست‌پذیری بهتر ⚙️

یکی از مهم‌ترین قدم‌ها برای Clean Code در پروژه‌های ASP.NET Core این است که وابستگی‌ها را مستقیم new نکنیم، بلکه از Dependency Injection استفاده کنیم. این کار هم کد را تمیزتر می‌کند، هم تست‌پذیری و امکان تغییر پیاده‌سازی‌ها را بالا می‌برد. 💡

فرض کنید در یک Web API کنترلری دارید که مستقیم وابسته به Service است:

public class OrdersController : ControllerBase
{
private readonly OrderService _orderService;

public OrdersController()
{
_orderService = new OrderService();
}

[HttpGet("api/orders/{id}")]
public async Task<IActionResult> Get(int id)
{
var order = await _orderService.GetByIdAsync(id);
return order is null ? NotFound() : Ok(order);
}
}


این طراحی چند مشکل جدی دارد:

• کنترلر مستقیماً به OrderService Couple شده است.
• تست Unit برای کنترلر سخت است، چون نمی‌توان Mock تزریق کرد.
• در آینده اگر سرویس عوض شود (مثلاً کش اضافه شود)، باید خود کنترلر را تغییر بدهید.

راه تمیزتر این است که یک Interface تعریف کنید و آن را از DI Container تزریق کنید:

public interface IOrderService
{
Task<Order?> GetByIdAsync(int id);
}

public class OrderService : IOrderService
{
private readonly AppDbContext _db;

public OrderService(AppDbContext db)
{
_db = db;
}

public Task<Order?> GetByIdAsync(int id)
=> _db.Orders.FindAsync(id).AsTask();
}


حالا کنترلر فقط Interface را می‌شناسد:

[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly IOrderService _orderService;

public OrdersController(IOrderService orderService)
{
_orderService = orderService;
}

[HttpGet("{id}")]
public async Task<IActionResult> Get(int id)
{
var order = await _orderService.GetByIdAsync(id);
return order is null ? NotFound() : Ok(order);
}
}


و در Program.cs (یا Startup قدیمی‌ها):

builder.Services.AddScoped<IOrderService, OrderService>();


مزیت‌ها 🧩

• تست‌پذیری بالا: در تست‌ها می‌توان یک FakeOrderService یا Mock تزریق کرد.
• پیاده‌سازی قابل‌تعویض: بدون تغییر کنترلر می‌توانید CachingOrderService اضافه کنید.
• رعایت اصل D از SOLID (Dependency Inversion): وابستگی به Abstraction نه Concrete Class.

برای مطالعه بیشتر، مستندات رسمی مایکروسافت درباره DI در ASP.NET Core خیلی شفاف و به‌روز است:
Microsoft Learn - Dependency injection in ASP.NET Core

قدم‌به‌قدم اگر وابستگی‌های مستقیم را به شکل Interface + DI تبدیل کنید، معماری اپلیکیشن تمیزتر، منعطف‌تر و آماده‌ٔ رشد می‌شود. 🚀

🔖 #CSharp #سی_شارپ #C# #_NET #ASP_NET_Core #Dependency_Injection #SOLID #Clean_Code

👤 Developix

💎 Channel: @DevelopixCSharp
👍31