عنوان:

‫آیا استفاده از MediatR در هر پروژه‌ای ضروری یا حتی بهینه است؟


نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۰۱/۱۷ ۰۸:۴۲
آدرس: www.dntips.ir
مقدمه
در سال‌های اخیر، الگوهای معماری مانند معماری تمیز (Clean Architecture) و جداسازی مسئولیت‌های دستور و پرس‌وجو (CQRS) در اکوسیستم .NET به شدت مورد توجه قرار گرفته‌اند. در این میان، کتابخانه MediatR به عنوان ابزاری محبوب برای پیاده‌سازی الگوی واسطه (Mediator Pattern) و پشتیبانی از CQRS، در بسیاری از پروژه‌ها به کار گرفته شده است. این کتابخانه با هدف کاهش وابستگی‌های مستقیم بین اجزای برنامه و ایجاد ساختاری منسجم و قابل تست، طراحی شده است. اما آیا استفاده از MediatR در هر پروژه‌ای ضروری یا حتی بهینه است؟ برخی معتقدند که این ابزار در پروژه‌های ساده می‌تواند پیچیدگی غیرضروری ایجاد کند، در حالی که دیگران آن را راه‌حلی قدرتمند برای مدیریت منطق برنامه می‌دانند. در این مقاله، با تحلیل دیدگاه‌های مختلف، به بررسی مزایا، معایب و شرایط استفاده از MediatR می‌پردازیم تا به توسعه‌دهندگان .NET کمک کنیم تصمیم آگاهانه‌تری بگیرند.

MediatR چیست و چگونه کار می‌کند؟
MediatR یک کتابخانه متن‌باز (Open-Source) است که الگوی واسطه را در برنامه‌های .NET پیاده‌سازی می‌کند. این الگو با قرار دادن یک واسطه مرکزی بین فرستنده درخواست‌ها و گیرنده‌ها (Handlerها)، وابستگی مستقیم بین اجزای برنامه را حذف می‌کند. در عمل، MediatR به شما اجازه می‌دهد درخواست‌ها (Requests) مانند دستورات (Commands) یا پرس‌وجوها (Queries) را تعریف کنید و سپس این درخواست‌ها را به Handlerهای مربوطه ارسال کنید. به عنوان مثال، در یک پروژه ساده، می‌توانید یک دستور برای افزودن یک شیء جدید تعریف کنید:
public record AddGoatCommand(string Name, string Description) : ICommand<AddGoatResponse>;
public record AddGoatResponse(Guid GoatId);

public class AddGoatHandler : IHandler<AddGoatCommand, AddGoatResponse>
{
    private readonly IRepository _repository;
    public AddGoatHandler(IRepository repository) => _repository = repository;

    public Task<AddGoatResponse> Handle(AddGoatCommand request, CancellationToken cancellationToken)
    {
        var goat = new Goat { Name = request.Name, Description = request.Description };
        _repository.Save(goat);
        return Task.FromResult(new AddGoatResponse(goat.Id));
    }
}
در اینجا، واسطه درخواست را به Handler مناسب هدایت می‌کند، بدون اینکه فرستنده (مثلاً یک کنترلر) نیازی به شناخت مستقیم Handler داشته باشد. این رویکرد به ویژه در پروژه‌هایی با معماری تمیز، به دلیل جداسازی لایه‌ها و کاهش وابستگی‌ها، جذاب است.

مزایای استفاده از MediatR
یکی از بزرگ‌ترین مزایای MediatR، جداسازی واضح بین لایه‌های برنامه است. با استفاده از این کتابخانه، کنترلرها یا نقاط ورودی (Endpoints) نیازی به تزریق مستقیم سرویس‌ها ندارند و تنها با واسطه ارتباط برقرار می‌کنند. این موضوع تست‌پذیری (Testability) را بهبود می‌بخشد، زیرا می‌توانید Handlerها را به صورت جداگانه آزمایش کنید. به علاوه، MediatR امکان پیاده‌سازی رفتارهای سراسری (Pipeline Behaviors) مانند اعتبارسنجی (Validation) یا لاگ‌گذاری (Logging) را فراهم می‌کند که به صورت متمرکز اعمال می‌شوند.
برای مثال، فرض کنید بخواهید هنگام افزودن یک شیء، یک ردپای حسابرسی (Audit Trail) ثبت کنید. با MediatR می‌توانید این کار را به سادگی با تعریف یک رفتار (Behavior) انجام دهید، بدون اینکه منطق اصلی Handler تغییر کند. این انعطاف‌پذیری در پروژه‌های پیچیده که نیاز به مدیریت چندین نگرانی سراسری (Cross-Cutting Concerns) دارند، بسیار ارزشمند است.

معایب و چالش‌های MediatR
با وجود مزایا، استفاده از MediatR بدون چالش نیست. یکی از انتقادات اصلی، پیچیدگی اضافه‌ای است که به پروژه‌های ساده وارد می‌کند. در پروژه‌هایی که تعداد درخواست‌ها محدود است (مثلاً کمتر از 10 نقطه ورودی)، تعریف درخواست‌ها، پاسخ‌ها و Handlerها می‌تواند بیش از حد به نظر برسد. به عنوان مثال، در یک برنامه ساده که فقط چند عملیات CRUD (ایجاد، خواندن، به‌روزرسانی، حذف) انجام می‌دهد، استفاده از MediatR ممکن است زمان توسعه را افزایش دهد بدون اینکه ارزش قابل توجهی اضافه کند.
مثال زیر نشان می‌دهد که چگونه یک عملیات ساده بدون MediatR می‌تواند مستقیم‌تر باشد:
public class GoatService
{
    private readonly IRepository _repository;
    public GoatService(IRepository repository) => _repository = repository;

    public Guid AddGoat(string name, string description)
    {
        var goat = new Goat { Name = name, Description = description };
        _repository.Save(goat);
        return goat.Id;
    }
}
در اینجا، سرویس مستقیماً توسط کنترلر فراخوانی می‌شود و نیازی به واسطه نیست. این روش در پروژه‌های کوچک‌تر، خوانایی (Readability) و سادگی بیشتری دارد. اما منتقدان می‌گویند که با افزایش مقیاس پروژه، این رویکرد می‌تواند به وابستگی‌های پیچیده و کد سخت‌تر برای نگهداری منجر شود.

آیا MediatR برای CQRS ضروری است؟
یکی از سوءتفاهم‌های رایج این است که برای پیاده‌سازی CQRS حتماً باید از MediatR استفاده کرد. در واقع، CQRS یک الگوی معماری است که بر جداسازی عملیات خواندن (Queries) و نوشتن (Commands) تمرکز دارد و می‌تواند بدون هیچ کتابخانه‌ای پیاده‌سازی شود. برای مثال، می‌توانید به سادگی دو رابط جداگانه برای دستورات و پرس‌وجوها تعریف کنید:
public interface ICommand<T>
{
    Task<T> Execute();
}

public interface IQuery<T>
{
    Task<T> Fetch();
}
سپس، Handlerها را مستقیماً در کنترلرها تزریق کنید. این روش، انعطاف‌پذیری بالایی دارد و وابستگی به کتابخانه خارجی را حذف می‌کند. با این حال، MediatR با ارائه یک ساختار آماده و پشتیبانی از رفتارهای سراسری، این فرآیند را ساده‌تر می‌کند، به‌ویژه در پروژه‌هایی که نیاز به مقیاس‌پذیری (Scalability) دارند.

چه زمانی از MediatR استفاده کنیم؟
تصمیم به استفاده از MediatR باید بر اساس نیازهای پروژه باشد، نه صرفاً به دلیل محبوبیت آن. در پروژه‌های کوچک با تعداد محدودی نقطه ورودی (مثلاً کمتر از 20)، استفاده از سرویس‌های ساده یا معماری عمودی (Vertical Slice Architecture) می‌تواند کافی باشد. اما در پروژه‌های بزرگ‌تر که شامل ده‌ها یا صدها عملیات هستند، MediatR می‌تواند با کاهش وابستگی‌ها و افزایش قابلیت نگهداری (Maintainability)، ارزشمند باشد.
یک قانون کلی پیشنهادی این است: اگر پروژه شما بیش از 20 نقطه ورودی دارد یا نیاز به مدیریت پیچیده نگرانی‌های سراسری مانند لاگ‌گذاری، اعتبارسنجی یا رویدادهای دامنه (Domain Events) دارید، MediatR می‌تواند انتخاب مناسبی باشد. در غیر این صورت، رویکردهای ساده‌تر ممکن است کارآمدتر باشند.

جایگزین‌های MediatR
اگر تصمیم گرفتید از MediatR استفاده نکنید، گزینه‌های دیگری نیز وجود دارند. FastEndpoints یک کتابخانه متن‌باز است که رویکردی مشابه Minimal APIs ارائه می‌دهد، اما با کارایی بالاتر و قابلیت‌های بیشتر مانند اعتبارسنجی داخلی (Built-in Validation). همچنین، می‌توانید الگوی واسطه را خودتان پیاده‌سازی کنید. این کار نه تنها به شما کنترل کامل می‌دهد، بلکه درک عمیق‌تری از معماری برنامه‌تان فراهم می‌کند.

نتیجه‌گیری
MediatR ابزاری قدرتمند برای پیاده‌سازی الگوی واسطه و پشتیبانی از CQRS در پروژه‌های .NET است، اما استفاده از آن باید با دقت و بر اساس نیازهای خاص پروژه ارزیابی شود. در پروژه‌های کوچک و ساده، رویکردهای مستقیم‌تر می‌توانند زمان توسعه را کاهش دهند و پیچیدگی غیرضروری را حذف کنند. اما در پروژه‌های بزرگ‌تر و پیچیده، مزایای MediatR مانند جداسازی، تست‌پذیری و انعطاف‌پذیری، آن را به گزینه‌ای ارزشمند تبدیل می‌کند. توسعه‌دهندگان .NET باید با در نظر گرفتن مقیاس پروژه، نیازهای آینده و ترجیحات تیم، بین سادگی و قابلیت‌های پیشرفته تعادل برقرار کنند. در نهایت، انتخاب ابزار مناسب، نه دنبال کردن روندها، کلید موفقیت یک پروژه است.


مشاهده مطلب اصلی

نظرات

  • علی یگانه مقدم در ۱۴۰۴/۰۱/۱۷ ۱۵:۰۸
    البته باید دانست این بزرگ شدن پروژه رو هم شامل میشود یا نه
    یعنی الان پروژه کوچک هست ولی بعدا امکان بزرگ شدن آن وجود دارد یا خیر. توی ایران هم زیاد این اتفاق میفته و کسی هم برنمیگرده برای اصلاح یا تغییرات
    در اکثر پروژه ها این اتفاق زیاد میفتد. من خودم یک قالب آماده دارم که هر پروژه ای رو مبتنی بر اون جلو میبرم هم یکسان هست و هم آماده. فقط به نسبت آن پروژه تغییراتی ابتدایی روی آن میدهم.